home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkGrid.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  77.0 KB  |  2,612 lines  |  [TEXT/CWIE]

  1. /*
  2.  * tkGrid.c --
  3.  *
  4.  *    Grid based geometry manager.
  5.  *
  6.  * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
  7.  *
  8.  * See the file "license.terms" for information on usage and redistribution
  9.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  10.  *
  11.  * SCCS: @(#) tkGrid.c 1.38 97/06/25 19:34:25
  12.  */
  13.  
  14. #include "tkInt.h"
  15.  
  16. /*
  17.  * Convenience Macros
  18.  */
  19.  
  20. #ifdef MAX
  21. #   undef MAX
  22. #endif
  23. #define MAX(x,y)    ((x) > (y) ? (x) : (y))
  24. #ifdef MIN
  25. #   undef MIN
  26. #endif
  27. #define MIN(x,y)    ((x) > (y) ? (y) : (x))
  28.  
  29. #define COLUMN    (1)        /* working on column offsets */
  30. #define ROW    (2)        /* working on row offsets */
  31.  
  32. #define CHECK_ONLY    (1)    /* check max slot constraint */
  33. #define CHECK_SPACE    (2)    /* alloc more space, don't change max */
  34.  
  35. /*
  36.  * Pre-allocate enough row and column slots for "typical" sized tables
  37.  * this value should be chosen so by the time the extra malloc's are
  38.  * required, the layout calculations overwehlm them. [A "slot" contains
  39.  * information for either a row or column, depending upon the context.]
  40.  */
  41.  
  42. #define TYPICAL_SIZE    25  /* (arbitrary guess) */
  43. #define PREALLOC    10  /* extra slots to allocate */
  44.  
  45. /* 
  46.  * Data structures are allocated dynamically to support arbitrary sized tables.
  47.  * However, the space is proportional to the highest numbered slot with
  48.  * some non-default property.  This limit is used to head off mistakes and
  49.  * denial of service attacks by limiting the amount of storage required.
  50.  */
  51.  
  52. #define MAX_ELEMENT    10000
  53.  
  54. /*
  55.  * Special characters to support relative layouts.
  56.  */
  57.  
  58. #define REL_SKIP    'x'    /* Skip this column. */
  59. #define REL_HORIZ    '-'    /* Extend previous widget horizontally. */
  60. #define REL_VERT    '^'    /* Extend widget from row above. */
  61.  
  62. /*
  63.  *  Structure to hold information for grid masters.  A slot is either
  64.  *  a row or column.
  65.  */
  66.  
  67. typedef struct SlotInfo {
  68.     int minSize;        /* The minimum size of this slot (in pixels).
  69.                  * It is set via the rowconfigure or
  70.                  * columnconfigure commands. */
  71.     int weight;        /* The resize weight of this slot. (0) means
  72.                  * this slot doesn't resize. Extra space in
  73.                  * the layout is given distributed among slots
  74.                  * inproportion to their weights. */
  75.     int pad;        /* Extra padding, in pixels, required for
  76.                  * this slot.  This amount is "added" to the
  77.                  * largest slave in the slot. */
  78.     int offset;        /* This is a cached value used for
  79.                  * introspection.  It is the pixel
  80.                  * offset of the right or bottom edge
  81.                  * of this slot from the beginning of the
  82.                  * layout. */
  83.          int temp;        /* This is a temporary value used for
  84.                       * calculating adjusted weights when
  85.                       * shrinking the layout below its
  86.                       * nominal size. */
  87. } SlotInfo;
  88.  
  89. /*
  90.  * Structure to hold information during layout calculations.  There
  91.  * is one of these for each slot, an array for each of the rows or columns.
  92.  */
  93.  
  94. typedef struct GridLayout {
  95.     struct Gridder *binNextPtr;    /* The next slave window in this bin.
  96.                      * Each bin contains a list of all
  97.                      * slaves whose spans are >1 and whose
  98.                      * right edges fall in this slot. */
  99.     int minSize;        /* Minimum size needed for this slot,
  100.                      * in pixels.  This is the space required
  101.                      * to hold any slaves contained entirely
  102.                      * in this slot, adjusted for any slot
  103.                      * constrants, such as size or padding. */
  104.     int pad;            /* Padding needed for this slot */
  105.     int weight;            /* Slot weight, controls resizing. */
  106.     int minOffset;        /* The minimum offset, in pixels, from
  107.                      * the beginning of the layout to the
  108.                      * right/bottom edge of the slot calculated
  109.                      * from top/left to bottom/right. */
  110.     int maxOffset;        /* The maximum offset, in pixels, from
  111.                      * the beginning of the layout to the
  112.                      * right-or-bottom edge of the slot calculated
  113.                      * from bottom-or-right to top-or-left. */
  114. } GridLayout;
  115.  
  116. /*
  117.  * Keep one of these for each geometry master.
  118.  */
  119.  
  120. typedef struct {
  121.     SlotInfo *columnPtr;    /* Pointer to array of column constraints. */
  122.     SlotInfo *rowPtr;        /* Pointer to array of row constraints. */
  123.     int columnEnd;        /* The last column occupied by any slave. */
  124.     int columnMax;        /* The number of columns with constraints. */
  125.     int columnSpace;        /* The number of slots currently allocated for
  126.                      * column constraints. */
  127.     int rowEnd;            /* The last row occupied by any slave. */
  128.     int rowMax;            /* The number of rows with constraints. */
  129.     int rowSpace;        /* The number of slots currently allocated
  130.                      * for row constraints. */
  131.     int startX;            /* Pixel offset of this layout within its
  132.                      * parent. */
  133.     int startY;            /* Pixel offset of this layout within its
  134.                      * parent. */
  135. } GridMaster;
  136.  
  137. /*
  138.  * For each window that the grid cares about (either because
  139.  * the window is managed by the grid or because the window
  140.  * has slaves that are managed by the grid), there is a
  141.  * structure of the following type:
  142.  */
  143.  
  144. typedef struct Gridder {
  145.     Tk_Window tkwin;        /* Tk token for window.  NULL means that
  146.                  * the window has been deleted, but the
  147.                  * gridder hasn't had a chance to clean up
  148.                  * yet because the structure is still in
  149.                  * use. */
  150.     struct Gridder *masterPtr;    /* Master window within which this window
  151.                  * is managed (NULL means this window
  152.                  * isn't managed by the gridder). */
  153.     struct Gridder *nextPtr;    /* Next window managed within same
  154.                  * parent.  List order doesn't matter. */
  155.     struct Gridder *slavePtr;    /* First in list of slaves managed
  156.                  * inside this window (NULL means
  157.                  * no grid slaves). */
  158.     GridMaster *masterDataPtr;    /* Additional data for geometry master. */
  159.     int column, row;        /* Location in the grid (starting
  160.                  * from zero). */
  161.     int numCols, numRows;    /* Number of columns or rows this slave spans.
  162.                  * Should be at least 1. */
  163.     int padX, padY;        /* Total additional pixels to leave around the
  164.                  * window (half of this space is left on each
  165.                  * side).  This is space *outside* the window:
  166.                  * we'll allocate extra space in frame but
  167.                  * won't enlarge window). */
  168.     int iPadX, iPadY;        /* Total extra pixels to allocate inside the
  169.                  * window (half this amount will appear on
  170.                  * each side). */
  171.     int sticky;            /* which sides of its cavity this window
  172.                  * sticks to. See below for definitions */
  173.     int doubleBw;        /* Twice the window's last known border
  174.                  * width.  If this changes, the window
  175.                  * must be re-arranged within its parent. */
  176.     int *abortPtr;        /* If non-NULL, it means that there is a nested
  177.                  * call to ArrangeGrid already working on
  178.                  * this window.  *abortPtr may be set to 1 to
  179.                  * abort that nested call.  This happens, for
  180.                  * example, if tkwin or any of its slaves
  181.                  * is deleted. */
  182.     int flags;            /* Miscellaneous flags;  see below
  183.                  * for definitions. */
  184.  
  185.     /*
  186.      * These fields are used temporarily for layout calculations only.
  187.      */
  188.  
  189.     struct Gridder *binNextPtr;    /* Link to next span>1 slave in this bin. */
  190.     int size;            /* Nominal size (width or height) in pixels
  191.                      * of the slave.  This includes the padding. */
  192. } Gridder;
  193.  
  194. /* Flag values for "sticky"ness  The 16 combinations subsume the packer's
  195.  * notion of anchor and fill.
  196.  *
  197.  * STICK_NORTH      This window sticks to the top of its cavity.
  198.  * STICK_EAST        This window sticks to the right edge of its cavity.
  199.  * STICK_SOUTH        This window sticks to the bottom of its cavity.
  200.  * STICK_WEST        This window sticks to the left edge of its cavity.
  201.  */
  202.  
  203. #define STICK_NORTH        1
  204. #define STICK_EAST        2
  205. #define STICK_SOUTH        4
  206. #define STICK_WEST        8
  207.  
  208. /*
  209.  * Flag values for Grid structures:
  210.  *
  211.  * REQUESTED_RELAYOUT:        1 means a Tcl_DoWhenIdle request
  212.  *                has already been made to re-arrange
  213.  *                all the slaves of this window.
  214.  *
  215.  * DONT_PROPAGATE:        1 means don't set this window's requested
  216.  *                size.  0 means if this window is a master
  217.  *                then Tk will set its requested size to fit
  218.  *                the needs of its slaves.
  219.  */
  220.  
  221. #define REQUESTED_RELAYOUT    1
  222. #define DONT_PROPAGATE        2
  223.  
  224. /*
  225.  * Hash table used to map from Tk_Window tokens to corresponding
  226.  * Grid structures:
  227.  */
  228.  
  229. static Tcl_HashTable gridHashTable;
  230. static int initialized = 0;
  231.  
  232. /*
  233.  * Prototypes for procedures used only in this file:
  234.  */
  235.  
  236. static void    AdjustForSticky _ANSI_ARGS_((Gridder *slavePtr, int *xPtr, 
  237.             int *yPtr, int *widthPtr, int *heightPtr));
  238. static int    AdjustOffsets _ANSI_ARGS_((int width,
  239.             int elements, SlotInfo *slotPtr));
  240. static void    ArrangeGrid _ANSI_ARGS_((ClientData clientData));
  241. static int    CheckSlotData _ANSI_ARGS_((Gridder *masterPtr, int slot,
  242.             int slotType, int checkOnly));
  243. static int    ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
  244.             Tk_Window tkwin, int argc, char *argv[]));
  245. static void    DestroyGrid _ANSI_ARGS_((char *memPtr));
  246. static Gridder *GetGrid _ANSI_ARGS_((Tk_Window tkwin));
  247. static void    GridStructureProc _ANSI_ARGS_((
  248.             ClientData clientData, XEvent *eventPtr));
  249. static void    GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
  250.             Tk_Window tkwin));
  251. static void    GridReqProc _ANSI_ARGS_((ClientData clientData,
  252.             Tk_Window tkwin));
  253. static void     InitMasterData _ANSI_ARGS_((Gridder *masterPtr));
  254. static int    ResolveConstraints _ANSI_ARGS_((Gridder *gridPtr,
  255.             int rowOrColumn, int maxOffset));
  256. static void    SetGridSize _ANSI_ARGS_((Gridder *gridPtr));
  257. static void    StickyToString _ANSI_ARGS_((int flags, char *result));
  258. static int    StringToSticky _ANSI_ARGS_((char *string));
  259. static void    Unlink _ANSI_ARGS_((Gridder *gridPtr));
  260.  
  261. static Tk_GeomMgr gridMgrType = {
  262.     "grid",            /* name */
  263.     GridReqProc,        /* requestProc */
  264.     GridLostSlaveProc,        /* lostSlaveProc */
  265. };
  266.  
  267. /*
  268.  *--------------------------------------------------------------
  269.  *
  270.  * Tk_GridCmd --
  271.  *
  272.  *    This procedure is invoked to process the "grid" Tcl command.
  273.  *    See the user documentation for details on what it does.
  274.  *
  275.  * Results:
  276.  *    A standard Tcl result.
  277.  *
  278.  * Side effects:
  279.  *    See the user documentation.
  280.  *
  281.  *--------------------------------------------------------------
  282.  */
  283.  
  284. int
  285. Tk_GridCmd(clientData, interp, argc, argv)
  286.     ClientData clientData;    /* Main window associated with
  287.                  * interpreter. */
  288.     Tcl_Interp *interp;        /* Current interpreter. */
  289.     int argc;            /* Number of arguments. */
  290.     char **argv;        /* Argument strings. */
  291. {
  292.     Tk_Window tkwin = (Tk_Window) clientData;
  293.     Gridder *masterPtr;        /* master grid record */
  294.     GridMaster *gridPtr;    /* pointer to grid data */
  295.     size_t length;        /* streing length of argument */
  296.     char c;            /* 1st character of argument */
  297.   
  298.     if ((argc >= 2) && ((argv[1][0] == '.') || (argv[1][0] == REL_SKIP) ||
  299.             (argv[1][0] == REL_VERT))) {
  300.     return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
  301.     }
  302.     if (argc < 3) {
  303.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  304.         argv[0], " option arg ?arg ...?\"", (char *) NULL);
  305.     return TCL_ERROR;
  306.     }
  307.     c = argv[1][0];
  308.     length = strlen(argv[1]);
  309.   
  310.     if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
  311.     Tk_Window master;
  312.     int row, column;    /* origin for bounding box */
  313.     int row2, column2;    /* end of bounding box */
  314.     int endX, endY;        /* last column/row in the layout */
  315.     int x=0, y=0;        /* starting pixels for this bounding box */
  316.     int width, height;    /* size of the bounding box */
  317.  
  318.     if (argc!=3 && argc != 5 && argc != 7) {
  319.         Tcl_AppendResult(interp, "wrong number of arguments: ",
  320.             "must be \"",argv[0],
  321.             " bbox master ?column row ?column row??\"",
  322.             (char *) NULL);
  323.         return TCL_ERROR;
  324.     }
  325.         
  326.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  327.     if (master == NULL) {
  328.         return TCL_ERROR;
  329.     }
  330.     masterPtr = GetGrid(master);
  331.  
  332.     if (argc >= 5) {
  333.         if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
  334.         return TCL_ERROR;
  335.         }
  336.         if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
  337.         return TCL_ERROR;
  338.         }
  339.         column2 = column;
  340.         row2 = row;
  341.     }
  342.  
  343.     if (argc == 7) {
  344.         if (Tcl_GetInt(interp, argv[5], &column2) != TCL_OK) {
  345.         return TCL_ERROR;
  346.         }
  347.         if (Tcl_GetInt(interp, argv[6], &row2) != TCL_OK) {
  348.         return TCL_ERROR;
  349.         }
  350.     }
  351.  
  352.     gridPtr = masterPtr->masterDataPtr;
  353.     if (gridPtr == NULL) {
  354.         sprintf(interp->result, "%d %d %d %d",0,0,0,0);
  355.         return(TCL_OK);
  356.     }
  357.  
  358.     SetGridSize(masterPtr);
  359.     endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
  360.     endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
  361.  
  362.     if ((endX == 0) || (endY == 0)) {
  363.         sprintf(interp->result, "%d %d %d %d",0,0,0,0);
  364.         return(TCL_OK);
  365.     }
  366.     if (argc == 3) {
  367.         row = column = 0;
  368.         row2 = endY;
  369.         column2 = endX;
  370.     }
  371.  
  372.     if (column > column2) {
  373.         int temp = column;
  374.         column = column2, column2 = temp;
  375.     }
  376.     if (row > row2) {
  377.         int temp = row;
  378.         row = row2, row2 = temp;
  379.     }
  380.  
  381.     if (column > 0 && column < endX) {
  382.         x = gridPtr->columnPtr[column-1].offset;
  383.     } else if  (column > 0) {
  384.         x = gridPtr->columnPtr[endX-1].offset;
  385.     }
  386.  
  387.     if (row > 0 && row < endY) {
  388.         y = gridPtr->rowPtr[row-1].offset;
  389.     } else if (row > 0) {
  390.         y = gridPtr->rowPtr[endY-1].offset;
  391.     }
  392.  
  393.     if (column2 < 0) {
  394.         width = 0;
  395.     } else if (column2 >= endX) {
  396.         width = gridPtr->columnPtr[endX-1].offset - x;
  397.     } else {
  398.         width = gridPtr->columnPtr[column2].offset - x;
  399.     } 
  400.  
  401.     if (row2 < 0) {
  402.         height = 0;
  403.     } else if (row2 >= endY) {
  404.         height = gridPtr->rowPtr[endY-1].offset - y;
  405.     } else {
  406.         height = gridPtr->rowPtr[row2].offset - y;
  407.     } 
  408.  
  409.     sprintf(interp->result, "%d %d %d %d",
  410.         x + gridPtr->startX, y + gridPtr->startY, width, height);
  411.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  412.     if (argv[2][0] != '.') {
  413.         Tcl_AppendResult(interp, "bad argument \"", argv[2],
  414.             "\": must be name of window", (char *) NULL);
  415.         return TCL_ERROR;
  416.     }
  417.     return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
  418.     } else if (((c == 'f') && (strncmp(argv[1], "forget", length) == 0))  || 
  419.         ((c == 'r') && (strncmp(argv[1], "remove", length) == 0))) {
  420.     Tk_Window slave;
  421.     Gridder *slavePtr;
  422.     int i;
  423.     
  424.     for (i = 2; i < argc; i++) {
  425.         slave = Tk_NameToWindow(interp, argv[i], tkwin);
  426.         if (slave == NULL) {
  427.         return TCL_ERROR;
  428.         }
  429.         slavePtr = GetGrid(slave);
  430.         if (slavePtr->masterPtr != NULL) {
  431.  
  432.             /*
  433.              * For "forget", reset all the settings to their defaults
  434.              */
  435.  
  436.             if (c == 'f') {
  437.             slavePtr->column = slavePtr->row = -1;
  438.             slavePtr->numCols = 1;
  439.             slavePtr->numRows = 1;
  440.             slavePtr->padX = slavePtr->padY = 0;
  441.             slavePtr->iPadX = slavePtr->iPadY = 0;
  442.             slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
  443.             slavePtr->flags = 0;
  444.             slavePtr->sticky = 0;
  445.             }
  446.         Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
  447.             (ClientData) NULL);
  448.         Unlink(slavePtr);
  449.         Tk_UnmapWindow(slavePtr->tkwin);
  450.         }
  451.     }
  452.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  453.     register Gridder *slavePtr;
  454.     Tk_Window slave;
  455.     char buffer[70];
  456.     
  457.     if (argc != 3) {
  458.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  459.             argv[0], " info window\"", (char *) NULL);
  460.         return TCL_ERROR;
  461.     }
  462.     slave = Tk_NameToWindow(interp, argv[2], tkwin);
  463.     if (slave == NULL) {
  464.         return TCL_ERROR;
  465.     }
  466.     slavePtr = GetGrid(slave);
  467.     if (slavePtr->masterPtr == NULL) {
  468.         interp->result[0] = '\0';
  469.         return TCL_OK;
  470.     }
  471.     
  472.     Tcl_AppendElement(interp, "-in");
  473.     Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
  474.     sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
  475.         slavePtr->column, slavePtr->row,
  476.         slavePtr->numCols, slavePtr->numRows);
  477.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  478.     sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
  479.         slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
  480.         slavePtr->padY/2);
  481.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  482.     StickyToString(slavePtr->sticky,buffer);
  483.     Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
  484.     } else if((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
  485.     Tk_Window master;
  486.     register SlotInfo *slotPtr;
  487.     int x, y;        /* Offset in pixels, from edge of parent. */
  488.     int i, j;        /* Corresponding column and row indeces. */
  489.     int endX, endY;        /* end of grid */
  490.  
  491.     if (argc != 5) {
  492.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  493.             argv[0], " location master x y\"", (char *)NULL);
  494.         return TCL_ERROR;
  495.     }
  496.  
  497.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  498.     if (master == NULL) {
  499.         return TCL_ERROR;
  500.     }
  501.  
  502.     if (Tk_GetPixels(interp, master, argv[3], &x) != TCL_OK) {
  503.         return TCL_ERROR;
  504.     }
  505.     if (Tk_GetPixels(interp, master, argv[4], &y) != TCL_OK) {
  506.         return TCL_ERROR;
  507.     }
  508.  
  509.     masterPtr = GetGrid(master);
  510.     if (masterPtr->masterDataPtr == NULL) {
  511.         sprintf(interp->result, "%d %d", -1, -1);
  512.         return TCL_OK;
  513.     }
  514.     gridPtr = masterPtr->masterDataPtr;
  515.  
  516.     /* 
  517.      * Update any pending requests.  This is not always the
  518.      * steady state value, as more configure events could be in
  519.      * the pipeline, but its as close as its easy to get.
  520.      */
  521.  
  522.     while (masterPtr->flags & REQUESTED_RELAYOUT) {
  523.         Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
  524.         ArrangeGrid ((ClientData) masterPtr);
  525.     }
  526.     SetGridSize(masterPtr);
  527.     endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
  528.     endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
  529.  
  530.     slotPtr  = masterPtr->masterDataPtr->columnPtr;
  531.     if (x < masterPtr->masterDataPtr->startX) {
  532.         i = -1;
  533.     } else {
  534.         x -= masterPtr->masterDataPtr->startX;
  535.         for (i=0;slotPtr[i].offset < x && i < endX; i++) {
  536.         /* null body */
  537.         }
  538.     }
  539.  
  540.     slotPtr  = masterPtr->masterDataPtr->rowPtr;
  541.     if (y < masterPtr->masterDataPtr->startY) {
  542.         j = -1;
  543.     } else {
  544.         y -= masterPtr->masterDataPtr->startY;
  545.         for (j=0;slotPtr[j].offset < y && j < endY; j++) {
  546.         /* null body */
  547.         }
  548.     }
  549.  
  550.     sprintf(interp->result, "%d %d", i, j);
  551.     } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
  552.     Tk_Window master;
  553.     int propagate;
  554.     
  555.     if (argc > 4) {
  556.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  557.             argv[0], " propagate window ?boolean?\"",
  558.             (char *) NULL);
  559.         return TCL_ERROR;
  560.     }
  561.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  562.     if (master == NULL) {
  563.         return TCL_ERROR;
  564.     }
  565.     masterPtr = GetGrid(master);
  566.     if (argc == 3) {
  567.         interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
  568.         return TCL_OK;
  569.     }
  570.     if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
  571.         return TCL_ERROR;
  572.     }
  573.     if ((!propagate) ^ (masterPtr->flags&DONT_PROPAGATE)) {
  574.         masterPtr->flags  ^= DONT_PROPAGATE;
  575.       
  576.         /*
  577.          * Re-arrange the master to allow new geometry information to
  578.          * propagate upwards to the master's master.
  579.          */
  580.       
  581.         if (masterPtr->abortPtr != NULL) {
  582.         *masterPtr->abortPtr = 1;
  583.         }
  584.         if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  585.         masterPtr->flags |= REQUESTED_RELAYOUT;
  586.         Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  587.         }
  588.     }
  589.     } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)
  590.         && (length > 1)) {
  591.     Tk_Window master;
  592.  
  593.     if (argc != 3) {
  594.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  595.             argv[0], " size window\"", (char *) NULL);
  596.         return TCL_ERROR;
  597.     }
  598.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  599.     if (master == NULL) {
  600.         return TCL_ERROR;
  601.     }
  602.     masterPtr = GetGrid(master);
  603.  
  604.     if (masterPtr->masterDataPtr != NULL) {
  605.         SetGridSize(masterPtr);
  606.         gridPtr = masterPtr->masterDataPtr;
  607.         sprintf(interp->result, "%d %d",
  608.         MAX(gridPtr->columnEnd, gridPtr->columnMax),
  609.         MAX(gridPtr->rowEnd, gridPtr->rowMax));
  610.     } else {
  611.         sprintf(interp->result, "%d %d",0, 0);
  612.     }
  613.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)
  614.         && (length > 1)) {
  615.     Tk_Window master;
  616.     Gridder *slavePtr;
  617.     int i, value;
  618.     int row = -1, column = -1;
  619.  
  620.     if ((argc < 3) || ((argc%2) == 0)) {
  621.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  622.             argv[0], " slaves window ?-option value...?\"",
  623.             (char *) NULL);
  624.         return TCL_ERROR;
  625.     }
  626.  
  627.     for (i=3; i<argc; i+=2) {
  628.         length = strlen(argv[i]);
  629.         if ((*argv[i] != '-') || (length < 2)) {
  630.         Tcl_AppendResult(interp, "invalid args: should be \"",
  631.             argv[0], " slaves window ?-option value...?\"",
  632.             (char *) NULL);
  633.         return TCL_ERROR;
  634.         }
  635.         if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
  636.         return TCL_ERROR;
  637.         }
  638.         if (value < 0) {
  639.         Tcl_AppendResult(interp, argv[i],
  640.             " is an invalid value: should NOT be < 0",
  641.             (char *) NULL);
  642.         return TCL_ERROR;
  643.         }
  644.         if (strncmp(argv[i], "-column", length) == 0) {
  645.         column = value;
  646.         } else if (strncmp(argv[i], "-row", length) == 0) {
  647.         row = value;
  648.         } else {
  649.         Tcl_AppendResult(interp, argv[i],
  650.             " is an invalid option: should be \"",
  651.             "-row, -column\"",
  652.             (char *) NULL);
  653.         return TCL_ERROR;
  654.         }
  655.     }
  656.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  657.     if (master == NULL) {
  658.         return TCL_ERROR;
  659.     }
  660.     masterPtr = GetGrid(master);
  661.  
  662.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  663.                          slavePtr = slavePtr->nextPtr) {
  664.         if (column>=0 && (slavePtr->column > column
  665.             || slavePtr->column+slavePtr->numCols-1 < column)) {
  666.         continue;
  667.         }
  668.         if (row>=0 && (slavePtr->row > row ||
  669.             slavePtr->row+slavePtr->numRows-1 < row)) {
  670.         continue;
  671.         }
  672.         Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
  673.     }
  674.  
  675.     /*
  676.      * Sample argument combinations:
  677.      *  grid columnconfigure <master> <index> -option
  678.      *  grid columnconfigure <master> <index> -option value -option value
  679.      *  grid rowconfigure <master> <index>
  680.      *  grid rowconfigure <master> <index> -option
  681.      *  grid rowconfigure <master> <index> -option value -option value.
  682.      */
  683.    
  684.     } else if(((c == 'c') && (strncmp(argv[1], "columnconfigure", length) == 0)
  685.         && (length >= 3)) ||
  686.             ((c == 'r') && (strncmp(argv[1], "rowconfigure", length) == 0) 
  687.             && (length >=2))) {
  688.     Tk_Window master;
  689.     SlotInfo *slotPtr = NULL;
  690.     int slot;        /* the column or row number */
  691.     size_t length;        /* the # of chars in the "-option" string */
  692.     int slotType;        /* COLUMN or ROW */
  693.     int size;        /* the configuration value */
  694.     int checkOnly;        /* check the size only */
  695.     int argcPtr;        /* Number of items in index list */
  696.     char **argvPtr;        /* array of indeces */
  697.     char **indexP;        /* String value of current index list item. */
  698.     int ok;            /* temporary TCL result code */
  699.     int i;
  700.  
  701.     if (((argc%2 != 0) && (argc>6)) || (argc < 4)) {
  702.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  703.             " ", argv[1], " master index ?-option value...?\"",
  704.             (char *)NULL);
  705.         return TCL_ERROR;
  706.     }
  707.  
  708.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  709.     if (master == NULL) {
  710.         return TCL_ERROR;
  711.     }
  712.  
  713.     if (Tcl_SplitList(interp, argv[3], &argcPtr, &argvPtr) != TCL_OK) {
  714.         return TCL_ERROR;
  715.     }
  716.  
  717.     checkOnly = ((argc == 4) || (argc == 5));
  718.     masterPtr = GetGrid(master);
  719.     slotType = (c == 'c') ? COLUMN : ROW;
  720.     if (checkOnly && argcPtr > 1) {
  721.         Tcl_AppendResult(interp, argv[3],
  722.             " must be a single element.", (char *) NULL);
  723.         Tcl_Free((char *)argvPtr);
  724.         return TCL_ERROR;
  725.     }
  726.     for (indexP=argvPtr; *indexP != NULL; indexP++) {
  727.         if (Tcl_GetInt(interp, *indexP, &slot) != TCL_OK) {
  728.         Tcl_Free((char *)argvPtr);
  729.         return TCL_ERROR;
  730.         }
  731.         ok = CheckSlotData(masterPtr, slot, slotType, checkOnly);
  732.         if ((ok!=TCL_OK) && ((argc<4) || (argc>5))) {
  733.         Tcl_AppendResult(interp, argv[0],
  734.             " ", argv[1], ": \"", *argvPtr,"\" is out of range",
  735.             (char *) NULL);
  736.         Tcl_Free((char *)argvPtr);
  737.         return TCL_ERROR;
  738.         } else if (ok == TCL_OK) {
  739.         slotPtr = (slotType == COLUMN) ?
  740.             masterPtr->masterDataPtr->columnPtr :
  741.             masterPtr->masterDataPtr->rowPtr;
  742.         }
  743.  
  744.         /*
  745.          * Return all of the options for this row or column.  If the
  746.          * request is out of range, return all 0's.
  747.          */
  748.  
  749.         if (argc == 4) {
  750.         Tcl_Free((char *)argvPtr);
  751.         }
  752.         if ((argc == 4) && (ok == TCL_OK)) {
  753.         sprintf(interp->result,"-minsize %d -pad %d -weight %d",
  754.             slotPtr[slot].minSize,slotPtr[slot].pad,
  755.             slotPtr[slot].weight);
  756.         return (TCL_OK);
  757.         } else if (argc == 4) {
  758.         sprintf(interp->result,"-minsize %d -pad %d -weight %d", 0,0,0);
  759.         return (TCL_OK);
  760.         }
  761.  
  762.         /*
  763.          * Loop through each option value pair, setting the values as required.
  764.          * If only one option is given, with no value, the current value is
  765.          * returned.
  766.          */
  767.  
  768.         for (i=4; i<argc; i+=2) {
  769.         length = strlen(argv[i]);
  770.         if ((*argv[i] != '-') || length < 2) {
  771.             Tcl_AppendResult(interp, "invalid arg \"",
  772.                 argv[i], "\" :expecting -minsize, -pad, or -weight.",
  773.                 (char *) NULL);
  774.             Tcl_Free((char *)argvPtr);
  775.             return TCL_ERROR;
  776.         }
  777.         if (strncmp(argv[i], "-minsize", length) == 0) {
  778.             if (argc == 5) {
  779.                 int value =  ok == TCL_OK ? slotPtr[slot].minSize : 0;
  780.             sprintf(interp->result,"%d",value);
  781.             } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
  782.                 != TCL_OK) {
  783.             Tcl_Free((char *)argvPtr);
  784.             return TCL_ERROR;
  785.             } else {
  786.             slotPtr[slot].minSize = size;
  787.             }
  788.         }
  789.         else if (strncmp(argv[i], "-weight", length) == 0) {
  790.             int wt;
  791.             if (argc == 5) {
  792.                 int value =  ok == TCL_OK ? slotPtr[slot].weight : 0;
  793.             sprintf(interp->result,"%d",value);
  794.             } else if (Tcl_GetInt(interp, argv[i+1], &wt) != TCL_OK) {
  795.             Tcl_Free((char *)argvPtr);
  796.             return TCL_ERROR;
  797.             } else if (wt < 0) {
  798.             Tcl_AppendResult(interp, "invalid arg \"", argv[i],
  799.                 "\": should be non-negative", (char *) NULL);
  800.             Tcl_Free((char *)argvPtr);
  801.             return TCL_ERROR;
  802.             } else {
  803.             slotPtr[slot].weight = wt;
  804.             }
  805.         }
  806.         else if (strncmp(argv[i], "-pad", length) == 0) {
  807.             if (argc == 5) {
  808.                 int value =  ok == TCL_OK ? slotPtr[slot].pad : 0;
  809.             sprintf(interp->result,"%d",value);
  810.             } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
  811.                 != TCL_OK) {
  812.             Tcl_Free((char *)argvPtr);
  813.             return TCL_ERROR;
  814.             } else if (size < 0) {
  815.             Tcl_AppendResult(interp, "invalid arg \"", argv[i],
  816.                 "\": should be non-negative", (char *) NULL);
  817.             Tcl_Free((char *)argvPtr);
  818.             return TCL_ERROR;
  819.             } else {
  820.             slotPtr[slot].pad = size;
  821.             }
  822.         } else {
  823.             Tcl_AppendResult(interp, "invalid arg \"",
  824.                 argv[i], "\": expecting -minsize, -pad, or -weight.",
  825.                 (char *) NULL);
  826.             Tcl_Free((char *)argvPtr);
  827.             return TCL_ERROR;
  828.         }
  829.         }
  830.     }
  831.     Tcl_Free((char *)argvPtr);
  832.  
  833.     /*
  834.      * If we changed a property, re-arrange the table,
  835.      * and check for constraint shrinkage.
  836.      */
  837.  
  838.     if (argc != 5) {
  839.         if (slotType == ROW) {
  840.         int last = masterPtr->masterDataPtr->rowMax - 1;
  841.         while ((last >= 0) && (slotPtr[last].weight == 0)
  842.             && (slotPtr[last].pad == 0)
  843.             && (slotPtr[last].minSize == 0)) {
  844.             last--;
  845.         }
  846.         masterPtr->masterDataPtr->rowMax = last+1;
  847.         } else {
  848.         int last = masterPtr->masterDataPtr->columnMax - 1;
  849.         while ((last >= 0) && (slotPtr[last].weight == 0)
  850.             && (slotPtr[last].pad == 0)
  851.             && (slotPtr[last].minSize == 0)) {
  852.             last--;
  853.         }
  854.         masterPtr->masterDataPtr->columnMax = last + 1;
  855.         }
  856.  
  857.         if (masterPtr->abortPtr != NULL) {
  858.         *masterPtr->abortPtr = 1;
  859.         }
  860.         if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  861.         masterPtr->flags |= REQUESTED_RELAYOUT;
  862.         Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  863.         }
  864.     }
  865.     } else {
  866.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  867.         "\":  must be bbox, columnconfigure, configure, forget, info, ",
  868.         "location, propagate, remove, rowconfigure, size, or slaves.",
  869.         (char *) NULL);
  870.     return TCL_ERROR;
  871.     }
  872.     return TCL_OK;
  873. }
  874.  
  875. /*
  876.  *--------------------------------------------------------------
  877.  *
  878.  * GridReqProc --
  879.  *
  880.  *    This procedure is invoked by Tk_GeometryRequest for
  881.  *    windows managed by the grid.
  882.  *
  883.  * Results:
  884.  *    None.
  885.  *
  886.  * Side effects:
  887.  *    Arranges for tkwin, and all its managed siblings, to
  888.  *    be re-arranged at the next idle point.
  889.  *
  890.  *--------------------------------------------------------------
  891.  */
  892.  
  893. static void
  894. GridReqProc(clientData, tkwin)
  895.     ClientData clientData;    /* Grid's information about
  896.                  * window that got new preferred
  897.                  * geometry.  */
  898.     Tk_Window tkwin;        /* Other Tk-related information
  899.                  * about the window. */
  900. {
  901.     register Gridder *gridPtr = (Gridder *) clientData;
  902.  
  903.     gridPtr = gridPtr->masterPtr;
  904.     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
  905.     gridPtr->flags |= REQUESTED_RELAYOUT;
  906.     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
  907.     }
  908. }
  909.  
  910. /*
  911.  *--------------------------------------------------------------
  912.  *
  913.  * GridLostSlaveProc --
  914.  *
  915.  *    This procedure is invoked by Tk whenever some other geometry
  916.  *    claims control over a slave that used to be managed by us.
  917.  *
  918.  * Results:
  919.  *    None.
  920.  *
  921.  * Side effects:
  922.  *    Forgets all grid-related information about the slave.
  923.  *
  924.  *--------------------------------------------------------------
  925.  */
  926.  
  927. static void
  928. GridLostSlaveProc(clientData, tkwin)
  929.     ClientData clientData;    /* Grid structure for slave window that
  930.                  * was stolen away. */
  931.     Tk_Window tkwin;        /* Tk's handle for the slave window. */
  932. {
  933.     register Gridder *slavePtr = (Gridder *) clientData;
  934.  
  935.     if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
  936.     Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
  937.     }
  938.     Unlink(slavePtr);
  939.     Tk_UnmapWindow(slavePtr->tkwin);
  940. }
  941.  
  942. /*
  943.  *--------------------------------------------------------------
  944.  *
  945.  * AdjustOffsets --
  946.  *
  947.  *    This procedure adjusts the size of the layout to fit in the
  948.  *    space provided.  If it needs more space, the extra is added
  949.  *    according to the weights.  If it needs less, the space is removed
  950.  *    according to the weights, but at no time does the size drop below
  951.  *    the minsize specified for that slot.
  952.  *
  953.  * Results:
  954.  *    The initial offset of the layout,
  955.  *    if all the weights are zero, else 0.
  956.  *
  957.  * Side effects:
  958.  *    The slot offsets are modified to shrink the layout.
  959.  *
  960.  *--------------------------------------------------------------
  961.  */
  962.  
  963. static int
  964. AdjustOffsets(size, slots, slotPtr)
  965.     int size;            /* The total layout size (in pixels). */
  966.     int slots;            /* Number of slots. */
  967.     register SlotInfo *slotPtr;    /* Pointer to slot array. */
  968. {
  969.     register int slot;        /* Current slot. */
  970.     int diff;            /* Extra pixels needed to add to the layout. */
  971.     int totalWeight = 0;    /* Sum of the weights for all the slots. */
  972.     int weight = 0;        /* Sum of the weights so far. */
  973.     int minSize = 0;        /* Minimum possible layout size. */
  974.     int newDiff;        /* The most pixels that can be added on
  975.                      * the current pass. */
  976.  
  977.     diff = size - slotPtr[slots-1].offset;
  978.  
  979.     /*
  980.      * The layout is already the correct size; all done.
  981.      */
  982.  
  983.     if (diff == 0) {
  984.     return(0);
  985.     }
  986.  
  987.     /*
  988.      * If all the weights are zero, center the layout in its parent if 
  989.      * there is extra space, else clip on the bottom/right.
  990.      */
  991.  
  992.     for (slot=0; slot < slots; slot++) {
  993.     totalWeight += slotPtr[slot].weight;
  994.     }
  995.  
  996.     if (totalWeight == 0 ) {
  997.     return(diff > 0 ? diff/2 : 0);
  998.     }
  999.  
  1000.     /*
  1001.      * Add extra space according to the slot weights.  This is done
  1002.      * cumulatively to prevent round-off error accumulation.
  1003.      */
  1004.  
  1005.     if (diff > 0) {
  1006.     for (weight=slot=0; slot < slots; slot++) {
  1007.         weight += slotPtr[slot].weight;
  1008.         slotPtr[slot].offset += diff * weight / totalWeight;
  1009.     }
  1010.     return(0);
  1011.     }
  1012.  
  1013.     /*
  1014.      * The layout must shrink below its requested size.  Compute the
  1015.      * minimum possible size by looking at the slot minSizes.
  1016.      */
  1017.  
  1018.     for (slot=0; slot < slots; slot++) {
  1019.         if (slotPtr[slot].weight > 0) {
  1020.         minSize += slotPtr[slot].minSize;
  1021.     } else if (slot > 0) {
  1022.         minSize += slotPtr[slot].offset - slotPtr[slot-1].offset;
  1023.     } else {
  1024.         minSize += slotPtr[slot].offset;
  1025.     }
  1026.     }
  1027.  
  1028.     /*
  1029.      * If the requested size is less than the minimum required size,
  1030.      * set the slot sizes to their minimum values, then clip on the 
  1031.      * bottom/right.
  1032.      */
  1033.  
  1034.     if (size <= minSize) {
  1035.         int offset = 0;
  1036.     for (slot=0; slot < slots; slot++) {
  1037.         if (slotPtr[slot].weight > 0) {
  1038.         offset += slotPtr[slot].minSize;
  1039.         } else if (slot > 0) {
  1040.         offset += slotPtr[slot].offset - slotPtr[slot-1].offset;
  1041.         } else {
  1042.         offset += slotPtr[slot].offset;
  1043.         }
  1044.         slotPtr[slot].offset = offset;
  1045.     }
  1046.     return(0);
  1047.     }
  1048.  
  1049.     /*
  1050.      * Remove space from slots according to their weights.  The weights
  1051.      * get renormalized anytime a slot shrinks to its minimum size.
  1052.      */
  1053.  
  1054.     while (diff < 0) {
  1055.  
  1056.     /*
  1057.      * Find the total weight for the shrinkable slots.
  1058.      */
  1059.  
  1060.     for (totalWeight=slot=0; slot < slots; slot++) {
  1061.         int current = (slot == 0) ? slotPtr[slot].offset :
  1062.             slotPtr[slot].offset - slotPtr[slot-1].offset;
  1063.         if (current > slotPtr[slot].minSize) {
  1064.         totalWeight += slotPtr[slot].weight;
  1065.         slotPtr[slot].temp = slotPtr[slot].weight;
  1066.         } else {
  1067.         slotPtr[slot].temp = 0;
  1068.         }
  1069.     }
  1070.     if (totalWeight == 0) {
  1071.         break;
  1072.     }
  1073.  
  1074.     /*
  1075.      * Find the maximum amount of space we can distribute this pass.
  1076.      */
  1077.  
  1078.     newDiff = diff;
  1079.     for (slot = 0; slot < slots; slot++) {
  1080.         int current;        /* current size of this slot */
  1081.         int maxDiff;        /* max diff that would cause
  1082.                          * this slot to equal its minsize */
  1083.         if (slotPtr[slot].temp == 0) {
  1084.             continue;
  1085.         }
  1086.         current = (slot == 0) ? slotPtr[slot].offset :
  1087.             slotPtr[slot].offset - slotPtr[slot-1].offset;
  1088.         maxDiff = totalWeight * (slotPtr[slot].minSize - current)
  1089.             / slotPtr[slot].temp;
  1090.         if (maxDiff > newDiff) {
  1091.             newDiff = maxDiff;
  1092.         }
  1093.     }
  1094.  
  1095.     /*
  1096.      * Now distribute the space.
  1097.      */
  1098.  
  1099.     for (weight=slot=0; slot < slots; slot++) {
  1100.         weight += slotPtr[slot].temp;
  1101.         slotPtr[slot].offset += newDiff * weight / totalWeight;
  1102.     }
  1103.         diff -= newDiff;
  1104.     }
  1105.     return(0);
  1106. }
  1107.  
  1108. /*
  1109.  *--------------------------------------------------------------
  1110.  *
  1111.  * AdjustForSticky --
  1112.  *
  1113.  *    This procedure adjusts the size of a slave in its cavity based
  1114.  *    on its "sticky" flags.
  1115.  *
  1116.  * Results:
  1117.  *    The input x, y, width, and height are changed to represent the
  1118.  *    desired coordinates of the slave.
  1119.  *
  1120.  * Side effects:
  1121.  *    None.
  1122.  *
  1123.  *--------------------------------------------------------------
  1124.  */
  1125.  
  1126. static void
  1127. AdjustForSticky(slavePtr, xPtr, yPtr, widthPtr, heightPtr)
  1128.     Gridder *slavePtr;    /* Slave window to arrange in its cavity. */
  1129.     int *xPtr;        /* Pixel location of the left edge of the cavity. */
  1130.     int *yPtr;        /* Pixel location of the top edge of the cavity. */
  1131.     int *widthPtr;    /* Width of the cavity (in pixels). */
  1132.     int *heightPtr;    /* Height of the cavity (in pixels). */
  1133. {
  1134.     int diffx=0;    /* Cavity width - slave width. */
  1135.     int diffy=0;    /* Cavity hight - slave height. */
  1136.     int sticky = slavePtr->sticky;
  1137.  
  1138.     *xPtr += slavePtr->padX/2;
  1139.     *widthPtr -= slavePtr->padX;
  1140.     *yPtr += slavePtr->padY/2;
  1141.     *heightPtr -= slavePtr->padY;
  1142.  
  1143.     if (*widthPtr > (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX)) {
  1144.     diffx = *widthPtr - (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX);
  1145.     *widthPtr = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX;
  1146.     }
  1147.  
  1148.     if (*heightPtr > (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY)) {
  1149.     diffy = *heightPtr - (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY);
  1150.     *heightPtr = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY;
  1151.     }
  1152.  
  1153.     if (sticky&STICK_EAST && sticky&STICK_WEST) {
  1154.     *widthPtr += diffx;
  1155.     }
  1156.     if (sticky&STICK_NORTH && sticky&STICK_SOUTH) {
  1157.     *heightPtr += diffy;
  1158.     }
  1159.     if (!(sticky&STICK_WEST)) {
  1160.         *xPtr += (sticky&STICK_EAST) ? diffx : diffx/2;
  1161.     }
  1162.     if (!(sticky&STICK_NORTH)) {
  1163.         *yPtr += (sticky&STICK_SOUTH) ? diffy : diffy/2;
  1164.     }
  1165. }
  1166.  
  1167. /*
  1168.  *--------------------------------------------------------------
  1169.  *
  1170.  * ArrangeGrid --
  1171.  *
  1172.  *    This procedure is invoked (using the Tcl_DoWhenIdle
  1173.  *    mechanism) to re-layout a set of windows managed by
  1174.  *    the grid.  It is invoked at idle time so that a
  1175.  *    series of grid requests can be merged into a single
  1176.  *    layout operation.
  1177.  *
  1178.  * Results:
  1179.  *    None.
  1180.  *
  1181.  * Side effects:
  1182.  *    The slaves of masterPtr may get resized or moved.
  1183.  *
  1184.  *--------------------------------------------------------------
  1185.  */
  1186.  
  1187. static void
  1188. ArrangeGrid(clientData)
  1189.     ClientData clientData;    /* Structure describing parent whose slaves
  1190.                  * are to be re-layed out. */
  1191. {
  1192.     register Gridder *masterPtr = (Gridder *) clientData;
  1193.     register Gridder *slavePtr;    
  1194.     GridMaster *slotPtr = masterPtr->masterDataPtr;
  1195.     int abort;
  1196.     int width, height;        /* requested size of layout, in pixels */
  1197.     int realWidth, realHeight;    /* actual size layout should take-up */
  1198.  
  1199.     masterPtr->flags &= ~REQUESTED_RELAYOUT;
  1200.  
  1201.     /*
  1202.      * If the parent has no slaves anymore, then don't do anything
  1203.      * at all:  just leave the parent's size as-is.  Otherwise there is
  1204.      * no way to "relinquish" control over the parent so another geometry
  1205.      * manager can take over.
  1206.      */
  1207.  
  1208.     if (masterPtr->slavePtr == NULL) {
  1209.     return;
  1210.     }
  1211.  
  1212.     if (masterPtr->masterDataPtr == NULL) {
  1213.     return;
  1214.     }
  1215.  
  1216.     /*
  1217.      * Abort any nested call to ArrangeGrid for this window, since
  1218.      * we'll do everything necessary here, and set up so this call
  1219.      * can be aborted if necessary.  
  1220.      */
  1221.  
  1222.     if (masterPtr->abortPtr != NULL) {
  1223.     *masterPtr->abortPtr = 1;
  1224.     }
  1225.     masterPtr->abortPtr = &abort;
  1226.     abort = 0;
  1227.     Tcl_Preserve((ClientData) masterPtr);
  1228.  
  1229.     /*
  1230.      * Call the constraint engine to fill in the row and column offsets.
  1231.      */
  1232.  
  1233.     SetGridSize(masterPtr);
  1234.     width =  ResolveConstraints(masterPtr, COLUMN, 0);
  1235.     height = ResolveConstraints(masterPtr, ROW, 0);
  1236.     width += 2*Tk_InternalBorderWidth(masterPtr->tkwin);
  1237.     height += 2*Tk_InternalBorderWidth(masterPtr->tkwin);
  1238.  
  1239.     if (((width != Tk_ReqWidth(masterPtr->tkwin))
  1240.             || (height != Tk_ReqHeight(masterPtr->tkwin)))
  1241.             && !(masterPtr->flags & DONT_PROPAGATE)) {
  1242.         Tk_GeometryRequest(masterPtr->tkwin, width, height);
  1243.     if (width>1 && height>1) {
  1244.         masterPtr->flags |= REQUESTED_RELAYOUT;
  1245.         Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  1246.     }
  1247.     masterPtr->abortPtr = NULL;
  1248.     Tcl_Release((ClientData) masterPtr);
  1249.         return;
  1250.     }
  1251.  
  1252.     /*
  1253.      * If the currently requested layout size doesn't match the parent's
  1254.      * window size, then adjust the slot offsets according to the
  1255.      * weights.  If all of the weights are zero, center the layout in 
  1256.      * its parent.  I haven't decided what to do if the parent is smaller
  1257.      * than the requested size.
  1258.      */
  1259.  
  1260.     realWidth = Tk_Width(masterPtr->tkwin) -
  1261.         2*Tk_InternalBorderWidth(masterPtr->tkwin);
  1262.     realHeight = Tk_Height(masterPtr->tkwin) -
  1263.         2*Tk_InternalBorderWidth(masterPtr->tkwin);
  1264.     slotPtr->startX = AdjustOffsets(realWidth,
  1265.         MAX(slotPtr->columnEnd,slotPtr->columnMax), slotPtr->columnPtr);
  1266.     slotPtr->startY = AdjustOffsets(realHeight,
  1267.         MAX(slotPtr->rowEnd,slotPtr->rowMax), slotPtr->rowPtr);
  1268.     slotPtr->startX += Tk_InternalBorderWidth(masterPtr->tkwin);
  1269.     slotPtr->startY += Tk_InternalBorderWidth(masterPtr->tkwin);
  1270.  
  1271.     /*
  1272.      * Now adjust the actual size of the slave to its cavity by
  1273.      * computing the cavity size, and adjusting the widget according
  1274.      * to its stickyness.
  1275.      */
  1276.  
  1277.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort;
  1278.         slavePtr = slavePtr->nextPtr) {
  1279.     int x, y;            /* top left coordinate */
  1280.     int width, height;        /* slot or slave size */
  1281.     int col = slavePtr->column;
  1282.     int row = slavePtr->row;
  1283.  
  1284.     x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0;
  1285.     y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0;
  1286.  
  1287.     width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x;
  1288.     height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y;
  1289.  
  1290.         x += slotPtr->startX;
  1291.         y += slotPtr->startY;
  1292.  
  1293.     AdjustForSticky(slavePtr, &x, &y, &width, &height);
  1294.  
  1295.     /*
  1296.      * Now put the window in the proper spot.  (This was taken directly
  1297.      * from tkPack.c.)  If the slave is a child of the master, then
  1298.          * do this here.  Otherwise let Tk_MaintainGeometry do the work.
  1299.          */
  1300.  
  1301.         if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
  1302.             if ((width <= 0) || (height <= 0)) {
  1303.                 Tk_UnmapWindow(slavePtr->tkwin);
  1304.             } else {
  1305.                 if ((x != Tk_X(slavePtr->tkwin))
  1306.                         || (y != Tk_Y(slavePtr->tkwin))
  1307.                         || (width != Tk_Width(slavePtr->tkwin))
  1308.                         || (height != Tk_Height(slavePtr->tkwin))) {
  1309.                     Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
  1310.                 }
  1311.                 if (abort) {
  1312.                     break;
  1313.                 }
  1314.  
  1315.                 /*
  1316.                  * Don't map the slave if the master isn't mapped: wait
  1317.                  * until the master gets mapped later.
  1318.                  */
  1319.  
  1320.                 if (Tk_IsMapped(masterPtr->tkwin)) {
  1321.                     Tk_MapWindow(slavePtr->tkwin);
  1322.                 }
  1323.             }
  1324.         } else {
  1325.             if ((width <= 0) || (height <= 0)) {
  1326.                 Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
  1327.                 Tk_UnmapWindow(slavePtr->tkwin);
  1328.             } else {
  1329.                 Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
  1330.                         x, y, width, height);
  1331.             }
  1332.         }
  1333.     }
  1334.  
  1335.     masterPtr->abortPtr = NULL;
  1336.     Tcl_Release((ClientData) masterPtr);
  1337. }
  1338.  
  1339. /*
  1340.  *--------------------------------------------------------------
  1341.  *
  1342.  * ResolveConstraints --
  1343.  *
  1344.  *    Resolve all of the column and row boundaries.  Most of
  1345.  *    the calculations are identical for rows and columns, so this procedure
  1346.  *    is called twice, once for rows, and again for columns.
  1347.  *
  1348.  * Results:
  1349.  *    The offset (in pixels) from the left/top edge of this layout is
  1350.  *    returned.
  1351.  *
  1352.  * Side effects:
  1353.  *    The slot offsets are copied into the SlotInfo structure for the
  1354.  *    geometry master.
  1355.  *
  1356.  *--------------------------------------------------------------
  1357.  */
  1358.  
  1359. static int
  1360. ResolveConstraints(masterPtr, slotType, maxOffset) 
  1361.     Gridder *masterPtr;        /* The geometry master for this grid. */
  1362.     int slotType;        /* Either ROW or COLUMN. */
  1363.     int maxOffset;        /* The actual maximum size of this layout
  1364.                      * in pixels,  or 0 (not currently used). */
  1365. {
  1366.     register SlotInfo *slotPtr;    /* Pointer to row/col constraints. */
  1367.     register Gridder *slavePtr;    /* List of slave windows in this grid. */
  1368.     int constraintCount;    /* Count of rows or columns that have
  1369.                      * constraints. */
  1370.     int slotCount;        /* Last occupied row or column. */
  1371.     int gridCount;        /* The larger of slotCount and constraintCount.
  1372.                      */
  1373.     GridLayout *layoutPtr;    /* Temporary layout structure. */
  1374.     int requiredSize;        /* The natural size of the grid (pixels).
  1375.                  * This is the minimum size needed to
  1376.                  * accomodate all of the slaves at their
  1377.                  * requested sizes. */
  1378.     int offset;            /* The pixel offset of the right edge of the
  1379.                      * current slot from the beginning of the
  1380.                      * layout. */
  1381.     int slot;            /* The current slot. */
  1382.     int start;            /* The first slot of a contiguous set whose
  1383.                      * constraints are not yet fully resolved. */
  1384.     int end;            /* The Last slot of a contiguous set whose
  1385.                  * constraints are not yet fully resolved. */
  1386.  
  1387.     /*
  1388.      * For typical sized tables, we'll use stack space for the layout data
  1389.      * to avoid the overhead of a malloc and free for every layout.
  1390.      */
  1391.  
  1392.     GridLayout layoutData[TYPICAL_SIZE + 1];
  1393.  
  1394.     if (slotType == COLUMN) {
  1395.     constraintCount = masterPtr->masterDataPtr->columnMax;
  1396.     slotCount = masterPtr->masterDataPtr->columnEnd;
  1397.     slotPtr  = masterPtr->masterDataPtr->columnPtr;
  1398.     } else {
  1399.     constraintCount = masterPtr->masterDataPtr->rowMax;
  1400.     slotCount = masterPtr->masterDataPtr->rowEnd;
  1401.     slotPtr  = masterPtr->masterDataPtr->rowPtr;
  1402.     }
  1403.  
  1404.     /*
  1405.      * Make sure there is enough memory for the layout.
  1406.      */
  1407.  
  1408.     gridCount = MAX(constraintCount,slotCount);
  1409.     if (gridCount >= TYPICAL_SIZE) {
  1410.     layoutPtr = (GridLayout *) Tcl_Alloc(sizeof(GridLayout) * (1+gridCount));
  1411.     } else {
  1412.     layoutPtr = layoutData;
  1413.     }
  1414.  
  1415.     /*
  1416.      * Allocate an extra layout slot to represent the left/top edge of
  1417.      * the 0th slot to make it easier to calculate slot widths from
  1418.      * offsets without special case code.
  1419.      * Initialize the "dummy" slot to the left/top of the table.
  1420.      * This slot avoids special casing the first slot.
  1421.      */
  1422.  
  1423.     layoutPtr->minOffset = 0;
  1424.     layoutPtr->maxOffset = 0;
  1425.     layoutPtr++;
  1426.  
  1427.     /*
  1428.      * Step 1.
  1429.      * Copy the slot constraints into the layout structure,
  1430.      * and initialize the rest of the fields.
  1431.      */
  1432.  
  1433.     for (slot=0; slot < constraintCount; slot++) {
  1434.         layoutPtr[slot].minSize = slotPtr[slot].minSize;
  1435.         layoutPtr[slot].weight =  slotPtr[slot].weight;
  1436.         layoutPtr[slot].pad =  slotPtr[slot].pad;
  1437.         layoutPtr[slot].binNextPtr = NULL;
  1438.     }
  1439.     for(;slot<gridCount;slot++) {
  1440.         layoutPtr[slot].minSize = 0;
  1441.         layoutPtr[slot].weight = 0;
  1442.         layoutPtr[slot].pad = 0;
  1443.         layoutPtr[slot].binNextPtr = NULL;
  1444.     }
  1445.  
  1446.     /*
  1447.      * Step 2.
  1448.      * Slaves with a span of 1 are used to determine the minimum size of
  1449.      * each slot.  Slaves whose span is two or more slots don't
  1450.      * contribute to the minimum size of each slot directly, but can cause
  1451.      * slots to grow if their size exceeds the the sizes of the slots they
  1452.      * span.
  1453.      * 
  1454.      * Bin all slaves whose spans are > 1 by their right edges.  This
  1455.      * allows the computation on minimum and maximum possible layout
  1456.      * sizes at each slot boundary, without the need to re-sort the slaves.
  1457.      */
  1458.  
  1459.     switch (slotType) {
  1460.         case COLUMN:
  1461.         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  1462.             slavePtr = slavePtr->nextPtr) {
  1463.         int rightEdge = slavePtr->column + slavePtr->numCols - 1;
  1464.         slavePtr->size = Tk_ReqWidth(slavePtr->tkwin) +
  1465.             slavePtr->padX + slavePtr->iPadX + slavePtr->doubleBw;
  1466.         if (slavePtr->numCols > 1) {
  1467.             slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
  1468.             layoutPtr[rightEdge].binNextPtr = slavePtr;
  1469.         } else {
  1470.             int size = slavePtr->size + layoutPtr[rightEdge].pad;
  1471.             if (size > layoutPtr[rightEdge].minSize) {
  1472.             layoutPtr[rightEdge].minSize = size;
  1473.             }
  1474.         }
  1475.         }
  1476.         break;
  1477.         case ROW:
  1478.         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  1479.             slavePtr = slavePtr->nextPtr) {
  1480.         int rightEdge = slavePtr->row + slavePtr->numRows - 1;
  1481.         slavePtr->size = Tk_ReqHeight(slavePtr->tkwin) +
  1482.             slavePtr->padY + slavePtr->iPadY + slavePtr->doubleBw;
  1483.         if (slavePtr->numRows > 1) {
  1484.             slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
  1485.             layoutPtr[rightEdge].binNextPtr = slavePtr;
  1486.         } else {
  1487.             int size = slavePtr->size + layoutPtr[rightEdge].pad;
  1488.             if (size > layoutPtr[rightEdge].minSize) {
  1489.             layoutPtr[rightEdge].minSize = size;
  1490.             }
  1491.         }
  1492.         }
  1493.         break;
  1494.     }
  1495.  
  1496.     /*
  1497.      * Step 3.
  1498.      * Determine the minimum slot offsets going from left to right
  1499.      * that would fit all of the slaves.  This determines the minimum
  1500.      */
  1501.  
  1502.     for (offset=slot=0; slot < gridCount; slot++) {
  1503.         layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset;
  1504.         for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
  1505.                     slavePtr = slavePtr->binNextPtr) {
  1506.         int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
  1507.             int required = slavePtr->size + layoutPtr[slot - span].minOffset;
  1508.             if (required > layoutPtr[slot].minOffset) {
  1509.                 layoutPtr[slot].minOffset = required;
  1510.             }
  1511.         }
  1512.         offset = layoutPtr[slot].minOffset;
  1513.     }
  1514.  
  1515.     /*
  1516.      * At this point, we know the minimum required size of the entire layout.
  1517.      * It might be prudent to stop here if our "master" will resize itself
  1518.      * to this size.
  1519.      */
  1520.  
  1521.     requiredSize = offset;
  1522.     if (maxOffset > offset) {
  1523.         offset=maxOffset;
  1524.     }
  1525.  
  1526.     /*
  1527.      * Step 4.
  1528.      * Determine the minimum slot offsets going from right to left,
  1529.      * bounding the pixel range of each slot boundary.
  1530.      * Pre-fill all of the right offsets with the actual size of the table;
  1531.      * they will be reduced as required.
  1532.      */
  1533.  
  1534.     for (slot=0; slot < gridCount; slot++) {
  1535.         layoutPtr[slot].maxOffset = offset;
  1536.     }
  1537.     for (slot=gridCount-1; slot > 0;) {
  1538.         for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
  1539.                     slavePtr = slavePtr->binNextPtr) {
  1540.         int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
  1541.             int require = offset - slavePtr->size;
  1542.             int startSlot  = slot - span;
  1543.             if (startSlot >=0 && require < layoutPtr[startSlot].maxOffset) {
  1544.                 layoutPtr[startSlot].maxOffset = require;
  1545.             }
  1546.     }
  1547.     offset -= layoutPtr[slot].minSize;
  1548.     slot--;
  1549.     if (layoutPtr[slot].maxOffset < offset) {
  1550.         offset = layoutPtr[slot].maxOffset;
  1551.     } else {
  1552.         layoutPtr[slot].maxOffset = offset;
  1553.     }
  1554.     }
  1555.  
  1556.     /*
  1557.      * Step 5.
  1558.      * At this point, each slot boundary has a range of values that
  1559.      * will satisfy the overall layout size.
  1560.      * Make repeated passes over the layout structure looking for
  1561.      * spans of slot boundaries where the minOffsets are less than
  1562.      * the maxOffsets, and adjust the offsets according to the slot
  1563.      * weights.  At each pass, at least one slot boundary will have
  1564.      * its range of possible values fixed at a single value.
  1565.      */
  1566.  
  1567.     for (start=0; start < gridCount;) {
  1568.         int totalWeight = 0;    /* Sum of the weights for all of the
  1569.                      * slots in this span. */
  1570.         int need = 0;        /* The minimum space needed to layout
  1571.                      * this span. */
  1572.         int have;        /* The actual amount of space that will
  1573.                      * be taken up by this span. */
  1574.         int weight;        /* Cumulative weights of the columns in 
  1575.                      * this span. */
  1576.         int noWeights = 0;    /* True if the span has no weights. */
  1577.  
  1578.         /*
  1579.          * Find a span by identifying ranges of slots whose edges are
  1580.          * already constrained at fixed offsets, but whose internal
  1581.          * slot boundaries have a range of possible positions.
  1582.          */
  1583.  
  1584.         if (layoutPtr[start].minOffset == layoutPtr[start].maxOffset) {
  1585.         start++;
  1586.         continue;
  1587.     }
  1588.  
  1589.     for (end=start+1; end<gridCount; end++) {
  1590.         if (layoutPtr[end].minOffset == layoutPtr[end].maxOffset) {
  1591.         break;
  1592.         }
  1593.     }
  1594.  
  1595.     /*
  1596.      * We found a span.  Compute the total weight, minumum space required,
  1597.      * for this span, and the actual amount of space the span should
  1598.      * use.
  1599.      */
  1600.  
  1601.     for (slot=start; slot<=end; slot++) {
  1602.         totalWeight += layoutPtr[slot].weight;
  1603.         need += layoutPtr[slot].minSize;
  1604.     }
  1605.     have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset;
  1606.  
  1607.     /*
  1608.      * If all the weights in the span are zero, then distribute the
  1609.      * extra space evenly.
  1610.      */
  1611.  
  1612.     if (totalWeight == 0) {
  1613.         noWeights++;
  1614.         totalWeight = end - start + 1;
  1615.     }
  1616.  
  1617.     /*
  1618.      * It might not be possible to give the span all of the space
  1619.      * available on this pass without violating the size constraints 
  1620.      * of one or more of the internal slot boundaries.
  1621.      * Determine the maximum amount of space that when added to the
  1622.      * entire span, would cause a slot boundary to have its possible
  1623.      * range reduced to one value, and reduce the amount of extra
  1624.      * space allocated on this pass accordingly.
  1625.      * 
  1626.      * The calculation is done cumulatively to avoid accumulating
  1627.      * roundoff errors.
  1628.      */
  1629.  
  1630.     for (weight=0,slot=start; slot<end; slot++) {
  1631.         int diff = layoutPtr[slot].maxOffset - layoutPtr[slot].minOffset;
  1632.         weight += noWeights ? 1 : layoutPtr[slot].weight;
  1633.         if ((noWeights || layoutPtr[slot].weight>0) &&
  1634.             (diff*totalWeight/weight) < (have-need)) {
  1635.         have = diff * totalWeight / weight + need;
  1636.         }
  1637.     }
  1638.  
  1639.     /*
  1640.      * Now distribute the extra space among the slots by
  1641.      * adjusting the minSizes and minOffsets.
  1642.      */
  1643.  
  1644.     for (weight=0,slot=start; slot<end; slot++) {
  1645.         weight += noWeights ? 1 : layoutPtr[slot].weight;
  1646.         layoutPtr[slot].minOffset +=
  1647.         (int)((double) (have-need) * weight/totalWeight + 0.5);
  1648.         layoutPtr[slot].minSize = layoutPtr[slot].minOffset 
  1649.             - layoutPtr[slot-1].minOffset;
  1650.     }
  1651.     layoutPtr[slot].minSize = layoutPtr[slot].minOffset 
  1652.         - layoutPtr[slot-1].minOffset;
  1653.  
  1654.     /*
  1655.      * Having pushed the top/left boundaries of the slots to
  1656.      * take up extra space, the bottom/right space is recalculated
  1657.      * to propagate the new space allocation.
  1658.      */
  1659.  
  1660.     for (slot=end; slot > start; slot--) {
  1661.         layoutPtr[slot-1].maxOffset = 
  1662.             layoutPtr[slot].maxOffset-layoutPtr[slot].minSize;
  1663.     }
  1664.     }
  1665.  
  1666.  
  1667.     /*
  1668.      * Step 6.
  1669.      * All of the space has been apportioned; copy the
  1670.      * layout information back into the master.
  1671.      */
  1672.  
  1673.     for (slot=0; slot < gridCount; slot++) {
  1674.         slotPtr[slot].offset = layoutPtr[slot].minOffset;
  1675.     }
  1676.  
  1677.     --layoutPtr;
  1678.     if (layoutPtr != layoutData) {
  1679.     Tcl_Free((char *)layoutPtr);
  1680.     }
  1681.     return requiredSize;
  1682. }
  1683.  
  1684. /*
  1685.  *--------------------------------------------------------------
  1686.  *
  1687.  * GetGrid --
  1688.  *
  1689.  *    This internal procedure is used to locate a Grid
  1690.  *    structure for a given window, creating one if one
  1691.  *    doesn't exist already.
  1692.  *
  1693.  * Results:
  1694.  *    The return value is a pointer to the Grid structure
  1695.  *    corresponding to tkwin.
  1696.  *
  1697.  * Side effects:
  1698.  *    A new grid structure may be created.  If so, then
  1699.  *    a callback is set up to clean things up when the
  1700.  *    window is deleted.
  1701.  *
  1702.  *--------------------------------------------------------------
  1703.  */
  1704.  
  1705. static Gridder *
  1706. GetGrid(tkwin)
  1707.     Tk_Window tkwin;        /* Token for window for which
  1708.                  * grid structure is desired. */
  1709. {
  1710.     register Gridder *gridPtr;
  1711.     Tcl_HashEntry *hPtr;
  1712.     int new;
  1713.  
  1714.     if (!initialized) {
  1715.     initialized = 1;
  1716.     Tcl_InitHashTable(&gridHashTable, TCL_ONE_WORD_KEYS);
  1717.     }
  1718.  
  1719.     /*
  1720.      * See if there's already grid for this window.  If not,
  1721.      * then create a new one.
  1722.      */
  1723.  
  1724.     hPtr = Tcl_CreateHashEntry(&gridHashTable, (char *) tkwin, &new);
  1725.     if (!new) {
  1726.     return (Gridder *) Tcl_GetHashValue(hPtr);
  1727.     }
  1728.     gridPtr = (Gridder *) Tcl_Alloc(sizeof(Gridder));
  1729.     gridPtr->tkwin = tkwin;
  1730.     gridPtr->masterPtr = NULL;
  1731.     gridPtr->masterDataPtr = NULL;
  1732.     gridPtr->nextPtr = NULL;
  1733.     gridPtr->slavePtr = NULL;
  1734.     gridPtr->binNextPtr = NULL;
  1735.  
  1736.     gridPtr->column = gridPtr->row = -1;
  1737.     gridPtr->numCols = 1;
  1738.     gridPtr->numRows = 1;
  1739.  
  1740.     gridPtr->padX = gridPtr->padY = 0;
  1741.     gridPtr->iPadX = gridPtr->iPadY = 0;
  1742.     gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
  1743.     gridPtr->abortPtr = NULL;
  1744.     gridPtr->flags = 0;
  1745.     gridPtr->sticky = 0;
  1746.     gridPtr->size = 0;
  1747.     gridPtr->masterDataPtr = NULL;
  1748.     Tcl_SetHashValue(hPtr, gridPtr);
  1749.     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
  1750.         GridStructureProc, (ClientData) gridPtr);
  1751.     return gridPtr;
  1752. }
  1753.  
  1754. /*
  1755.  *--------------------------------------------------------------
  1756.  *
  1757.  * SetGridSize --
  1758.  *
  1759.  *    This internal procedure sets the size of the grid occupied
  1760.  *    by slaves.
  1761.  *
  1762.  * Results:
  1763.  *    none
  1764.  *
  1765.  * Side effects:
  1766.  *    The width and height arguments are filled in the master data structure.
  1767.  *    Additional space is allocated for the constraints to accomodate
  1768.  *    the offsets.
  1769.  *
  1770.  *--------------------------------------------------------------
  1771.  */
  1772.  
  1773. static void
  1774. SetGridSize(masterPtr)
  1775.     Gridder *masterPtr;            /* The geometry master for this grid. */
  1776. {
  1777.     register Gridder *slavePtr;        /* Current slave window. */
  1778.     int maxX = 0, maxY = 0;
  1779.  
  1780.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  1781.         slavePtr = slavePtr->nextPtr) {
  1782.     maxX = MAX(maxX,slavePtr->numCols + slavePtr->column);
  1783.     maxY = MAX(maxY,slavePtr->numRows + slavePtr->row);
  1784.     }
  1785.     masterPtr->masterDataPtr->columnEnd = maxX;
  1786.     masterPtr->masterDataPtr->rowEnd = maxY;
  1787.     CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE);
  1788.     CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE);
  1789. }
  1790.  
  1791. /*
  1792.  *--------------------------------------------------------------
  1793.  *
  1794.  * CheckSlotData --
  1795.  *
  1796.  *    This internal procedure is used to manage the storage for
  1797.  *    row and column (slot) constraints.
  1798.  *
  1799.  * Results:
  1800.  *    TRUE if the index is OK, False otherwise.
  1801.  *
  1802.  * Side effects:
  1803.  *    A new master grid structure may be created.  If so, then
  1804.  *    it is initialized.  In addition, additional storage for
  1805.  *    a row or column constraints may be allocated, and the constraint
  1806.  *    maximums are adjusted.
  1807.  *
  1808.  *--------------------------------------------------------------
  1809.  */
  1810.  
  1811. static int
  1812. CheckSlotData(masterPtr, slot, slotType, checkOnly)
  1813.     Gridder *masterPtr;    /* the geometry master for this grid */
  1814.     int slot;        /* which slot to look at */
  1815.     int slotType;    /* ROW or COLUMN */
  1816.     int checkOnly;    /* don't allocate new space if true */
  1817. {
  1818.     int numSlot;        /* number of slots already allocated (Space) */
  1819.     int end;            /* last used constraint */
  1820.  
  1821.     /*
  1822.      * If slot is out of bounds, return immediately.
  1823.      */
  1824.  
  1825.     if (slot < 0 || slot >= MAX_ELEMENT) {
  1826.     return TCL_ERROR;
  1827.     }
  1828.  
  1829.     if ((checkOnly == CHECK_ONLY) && (masterPtr->masterDataPtr == NULL)) {
  1830.     return TCL_ERROR;
  1831.     }
  1832.  
  1833.     /*
  1834.      * If we need to allocate more space, allocate a little extra to avoid
  1835.      * repeated re-alloc's for large tables.  We need enough space to
  1836.      * hold all of the offsets as well.
  1837.      */
  1838.  
  1839.     InitMasterData(masterPtr);
  1840.     end = (slotType == ROW) ? masterPtr->masterDataPtr->rowMax :
  1841.         masterPtr->masterDataPtr->columnMax;
  1842.     if (checkOnly == CHECK_ONLY) {
  1843.         return  (end < slot) ? TCL_ERROR : TCL_OK;
  1844.     } else {
  1845.         numSlot = (slotType == ROW) ? masterPtr->masterDataPtr->rowSpace 
  1846.                                 : masterPtr->masterDataPtr->columnSpace;
  1847.         if (slot >= numSlot) {
  1848.         int      newNumSlot = slot + PREALLOC ;
  1849.         size_t   oldSize = numSlot    * sizeof(SlotInfo) ;
  1850.         size_t   newSize = newNumSlot * sizeof(SlotInfo) ;
  1851.         SlotInfo *new = (SlotInfo *) Tcl_Alloc(newSize);
  1852.         SlotInfo *old = (slotType == ROW) ?
  1853.             masterPtr->masterDataPtr->rowPtr :
  1854.             masterPtr->masterDataPtr->columnPtr;
  1855.         memcpy((VOID *) new, (VOID *) old, oldSize );
  1856.         memset((VOID *) (new+numSlot), 0, newSize - oldSize );
  1857.         Tcl_Free((char *) old);
  1858.         if (slotType == ROW) {
  1859.          masterPtr->masterDataPtr->rowPtr = new ;
  1860.             masterPtr->masterDataPtr->rowSpace = newNumSlot ;
  1861.         } else {
  1862.             masterPtr->masterDataPtr->columnPtr = new;
  1863.             masterPtr->masterDataPtr->columnSpace = newNumSlot ;
  1864.         }
  1865.     }
  1866.     if (slot >= end && checkOnly != CHECK_SPACE) {
  1867.         if (slotType == ROW) {
  1868.         masterPtr->masterDataPtr->rowMax = slot+1;
  1869.         } else {
  1870.         masterPtr->masterDataPtr->columnMax = slot+1;
  1871.         }
  1872.     }
  1873.         return TCL_OK;
  1874.     }
  1875. }
  1876.  
  1877. /*
  1878.  *--------------------------------------------------------------
  1879.  *
  1880.  * InitMasterData --
  1881.  *
  1882.  *    This internal procedure is used to allocate and initialize
  1883.  *    the data for a geometry master, if the data
  1884.  *    doesn't exist already.
  1885.  *
  1886.  * Results:
  1887.  *    none
  1888.  *
  1889.  * Side effects:
  1890.  *    A new master grid structure may be created.  If so, then
  1891.  *    it is initialized.
  1892.  *
  1893.  *--------------------------------------------------------------
  1894.  */
  1895.  
  1896. static void
  1897. InitMasterData(masterPtr)
  1898.     Gridder *masterPtr;
  1899. {
  1900.     size_t size;
  1901.     if (masterPtr->masterDataPtr == NULL) {
  1902.     GridMaster *gridPtr = masterPtr->masterDataPtr =
  1903.         (GridMaster *) Tcl_Alloc(sizeof(GridMaster));
  1904.     size = sizeof(SlotInfo) * TYPICAL_SIZE;
  1905.  
  1906.     gridPtr->columnEnd = 0;
  1907.     gridPtr->columnMax = 0;
  1908.     gridPtr->columnPtr = (SlotInfo *) Tcl_Alloc(size);
  1909.     gridPtr->columnSpace = 0;
  1910.     gridPtr->columnSpace = TYPICAL_SIZE;
  1911.     gridPtr->rowEnd = 0;
  1912.     gridPtr->rowMax = 0;
  1913.     gridPtr->rowPtr = (SlotInfo *) Tcl_Alloc(size);
  1914.     gridPtr->rowSpace = 0;
  1915.     gridPtr->rowSpace = TYPICAL_SIZE;
  1916.  
  1917.     memset((VOID *) gridPtr->columnPtr, 0, size);
  1918.     memset((VOID *) gridPtr->rowPtr, 0, size);
  1919.     }
  1920. }
  1921.  
  1922. /*
  1923.  *----------------------------------------------------------------------
  1924.  *
  1925.  * Unlink --
  1926.  *
  1927.  *    Remove a grid from its parent's list of slaves.
  1928.  *
  1929.  * Results:
  1930.  *    None.
  1931.  *
  1932.  * Side effects:
  1933.  *    The parent will be scheduled for re-arranging, and the size of the
  1934.  *    grid will be adjusted accordingly
  1935.  *
  1936.  *----------------------------------------------------------------------
  1937.  */
  1938.  
  1939. static void
  1940. Unlink(slavePtr)
  1941.     register Gridder *slavePtr;        /* Window to unlink. */
  1942. {
  1943.     register Gridder *masterPtr, *slavePtr2;
  1944.     GridMaster *gridPtr;    /* pointer to grid data */
  1945.  
  1946.     masterPtr = slavePtr->masterPtr;
  1947.     if (masterPtr == NULL) {
  1948.     return;
  1949.     }
  1950.  
  1951.     gridPtr = masterPtr->masterDataPtr;
  1952.     if (masterPtr->slavePtr == slavePtr) {
  1953.     masterPtr->slavePtr = slavePtr->nextPtr;
  1954.     }
  1955.     else {
  1956.     for (slavePtr2 = masterPtr->slavePtr; ; slavePtr2 = slavePtr2->nextPtr) {
  1957.         if (slavePtr2 == NULL) {
  1958.         panic("Unlink couldn't find previous window");
  1959.         }
  1960.         if (slavePtr2->nextPtr == slavePtr) {
  1961.         slavePtr2->nextPtr = slavePtr->nextPtr;
  1962.         break;
  1963.         }
  1964.     }
  1965.     }
  1966.     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  1967.     masterPtr->flags |= REQUESTED_RELAYOUT;
  1968.     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  1969.     }
  1970.     if (masterPtr->abortPtr != NULL) {
  1971.     *masterPtr->abortPtr = 1;
  1972.     }
  1973.  
  1974.     if ((slavePtr->numCols+slavePtr->column == gridPtr->columnMax)
  1975.         || (slavePtr->numRows+slavePtr->row == gridPtr->rowMax)) {
  1976.     }
  1977.     slavePtr->masterPtr = NULL;
  1978. }
  1979.  
  1980. /*
  1981.  *----------------------------------------------------------------------
  1982.  *
  1983.  * DestroyGrid --
  1984.  *
  1985.  *    This procedure is invoked by Tk_EventuallyFree or Tcl_Release
  1986.  *    to clean up the internal structure of a grid at a safe time
  1987.  *    (when no-one is using it anymore).   Cleaning up the grid involves
  1988.  *    freeing the main structure for all windows. and the master structure
  1989.  *    for geometry managers.
  1990.  *
  1991.  * Results:
  1992.  *    None.
  1993.  *
  1994.  * Side effects:
  1995.  *    Everything associated with the grid is freed up.
  1996.  *
  1997.  *----------------------------------------------------------------------
  1998.  */
  1999.  
  2000. static void
  2001. DestroyGrid(memPtr)
  2002.     char *memPtr;        /* Info about window that is now dead. */
  2003. {
  2004.     register Gridder *gridPtr = (Gridder *) memPtr;
  2005.  
  2006.     if (gridPtr->masterDataPtr != NULL) {
  2007.     if (gridPtr->masterDataPtr->rowPtr != NULL) {
  2008.         Tcl_Free((char *) gridPtr->masterDataPtr -> rowPtr);
  2009.     }
  2010.     if (gridPtr->masterDataPtr->columnPtr != NULL) {
  2011.         Tcl_Free((char *) gridPtr->masterDataPtr -> columnPtr);
  2012.     }
  2013.     Tcl_Free((char *) gridPtr->masterDataPtr);
  2014.     }
  2015.     Tcl_Free((char *) gridPtr);
  2016. }
  2017.  
  2018. /*
  2019.  *----------------------------------------------------------------------
  2020.  *
  2021.  * GridStructureProc --
  2022.  *
  2023.  *    This procedure is invoked by the Tk event dispatcher in response
  2024.  *    to StructureNotify events.
  2025.  *
  2026.  * Results:
  2027.  *    None.
  2028.  *
  2029.  * Side effects:
  2030.  *    If a window was just deleted, clean up all its grid-related
  2031.  *    information.  If it was just resized, re-configure its slaves, if
  2032.  *    any.
  2033.  *
  2034.  *----------------------------------------------------------------------
  2035.  */
  2036.  
  2037. static void
  2038. GridStructureProc(clientData, eventPtr)
  2039.     ClientData clientData;        /* Our information about window
  2040.                      * referred to by eventPtr. */
  2041.     XEvent *eventPtr;            /* Describes what just happened. */
  2042. {
  2043.     register Gridder *gridPtr = (Gridder *) clientData;
  2044.  
  2045.     if (eventPtr->type == ConfigureNotify) {
  2046.     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
  2047.         gridPtr->flags |= REQUESTED_RELAYOUT;
  2048.         Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
  2049.     }
  2050.     if (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width) {
  2051.         if ((gridPtr->masterPtr != NULL) &&
  2052.             !(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
  2053.         gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
  2054.         gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
  2055.         Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr);
  2056.         }
  2057.     }
  2058.     } else if (eventPtr->type == DestroyNotify) {
  2059.     register Gridder *gridPtr2, *nextPtr;
  2060.  
  2061.     if (gridPtr->masterPtr != NULL) {
  2062.         Unlink(gridPtr);
  2063.     }
  2064.     for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
  2065.                        gridPtr2 = nextPtr) {
  2066.         Tk_UnmapWindow(gridPtr2->tkwin);
  2067.         gridPtr2->masterPtr = NULL;
  2068.         nextPtr = gridPtr2->nextPtr;
  2069.         gridPtr2->nextPtr = NULL;
  2070.     }
  2071.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&gridHashTable,
  2072.         (char *) gridPtr->tkwin));
  2073.     if (gridPtr->flags & REQUESTED_RELAYOUT) {
  2074.         Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
  2075.     }
  2076.     gridPtr->tkwin = NULL;
  2077.     Tk_EventuallyFree((ClientData) gridPtr, DestroyGrid);
  2078.     } else if (eventPtr->type == MapNotify) {
  2079.     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
  2080.         gridPtr->flags |= REQUESTED_RELAYOUT;
  2081.         Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
  2082.     }
  2083.     } else if (eventPtr->type == UnmapNotify) {
  2084.     register Gridder *gridPtr2;
  2085.  
  2086.     for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
  2087.                        gridPtr2 = gridPtr2->nextPtr) {
  2088.         Tk_UnmapWindow(gridPtr2->tkwin);
  2089.     }
  2090.     }
  2091. }
  2092.  
  2093. /*
  2094.  *----------------------------------------------------------------------
  2095.  *
  2096.  * ConfigureSlaves --
  2097.  *
  2098.  *    This implements the guts of the "grid configure" command.  Given
  2099.  *    a list of slaves and configuration options, it arranges for the
  2100.  *    grid to manage the slaves and sets the specified options.
  2101.  *    arguments consist of windows or window shortcuts followed by
  2102.  *    "-option value" pairs.
  2103.  *
  2104.  * Results:
  2105.  *    TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
  2106.  *    returned and interp->result is set to contain an error message.
  2107.  *
  2108.  * Side effects:
  2109.  *    Slave windows get taken over by the grid.
  2110.  *
  2111.  *----------------------------------------------------------------------
  2112.  */
  2113.  
  2114. static int
  2115. ConfigureSlaves(interp, tkwin, argc, argv)
  2116.     Tcl_Interp *interp;        /* Interpreter for error reporting. */
  2117.     Tk_Window tkwin;        /* Any window in application containing
  2118.                  * slaves.  Used to look up slave names. */
  2119.     int argc;            /* Number of elements in argv. */
  2120.     char *argv[];        /* Argument strings:  contains one or more
  2121.                  * window names followed by any number
  2122.                  * of "option value" pairs.  Caller must
  2123.                  * make sure that there is at least one
  2124.                  * window name. */
  2125. {
  2126.     Gridder *masterPtr;
  2127.     Gridder *slavePtr;
  2128.     Tk_Window other, slave, parent, ancestor;
  2129.     int i, j, c, tmp;
  2130.     size_t length;
  2131.     int numWindows;
  2132.     int width;
  2133.     int defaultColumn = 0;    /* default column number */
  2134.     int defaultColumnSpan = 1;    /* default number of columns */
  2135.     char *lastWindow;        /* use this window to base current
  2136.                  * Row/col on */
  2137.  
  2138.     /*
  2139.      * Count the number of windows, or window short-cuts.
  2140.      */
  2141.  
  2142.     for(numWindows=i=0;i<argc;i++) {
  2143.         char firstChar = *argv[i];
  2144.     if (firstChar == '.') {
  2145.         numWindows++;
  2146.         continue;
  2147.         }
  2148.         length = strlen(argv[i]);
  2149.         if (length > 1 && firstChar == '-') {
  2150.         break;
  2151.     }
  2152.     if (length > 1) {
  2153.         Tcl_AppendResult(interp, "unexpected parameter, \"",
  2154.             argv[i], "\", in configure list. ",
  2155.             "Should be window name or option", (char *) NULL);
  2156.         return TCL_ERROR;
  2157.     }
  2158.  
  2159.     if ((firstChar == REL_HORIZ) && ((numWindows == 0) ||
  2160.         (*argv[i-1] == REL_SKIP) || (*argv[i-1] == REL_VERT))) {
  2161.         Tcl_AppendResult(interp,
  2162.             "Must specify window before shortcut '-'.",
  2163.             (char *) NULL);
  2164.         return TCL_ERROR;
  2165.     }
  2166.  
  2167.     if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)
  2168.         || (firstChar == REL_HORIZ)) {
  2169.         continue;
  2170.     }
  2171.  
  2172.     Tcl_AppendResult(interp, "invalid window shortcut, \"",
  2173.         argv[i], "\" should be '-', 'x', or '^'", (char *) NULL);
  2174.     return TCL_ERROR;
  2175.     }
  2176.     numWindows = i;
  2177.  
  2178.     if ((argc-numWindows)&1) {
  2179.     Tcl_AppendResult(interp, "extra option or",
  2180.         " option with no value", (char *) NULL);
  2181.     return TCL_ERROR;
  2182.     }
  2183.  
  2184.     /*
  2185.      * Iterate over all of the slave windows and short-cuts, parsing
  2186.      * options for each slave.  It's a bit wasteful to re-parse the
  2187.      * options for each slave, but things get too messy if we try to
  2188.      * parse the arguments just once at the beginning.  For example,
  2189.      * if a slave already is managed we want to just change a few
  2190.      * existing values without resetting everything.  If there are
  2191.      * multiple windows, the -in option only gets processed for the
  2192.      * first window.
  2193.      */
  2194.  
  2195.     masterPtr = NULL;
  2196.     for (j = 0; j < numWindows; j++) {
  2197.         char firstChar = *argv[j];
  2198.  
  2199.     /*
  2200.      * '^' and 'x' cause us to skip a column.  '-' is processed
  2201.      * as part of its preceeding slave.
  2202.      */
  2203.  
  2204.     if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)) {
  2205.         defaultColumn++;
  2206.         continue;
  2207.     }
  2208.     if (firstChar == REL_HORIZ) {
  2209.         continue;
  2210.     }
  2211.  
  2212.     for (defaultColumnSpan=1;
  2213.         j + defaultColumnSpan < numWindows &&
  2214.         (*argv[j+defaultColumnSpan] == REL_HORIZ);
  2215.         defaultColumnSpan++) {
  2216.         /* null body */
  2217.     }
  2218.  
  2219.     slave = Tk_NameToWindow(interp, argv[j], tkwin);
  2220.     if (slave == NULL) {
  2221.         return TCL_ERROR;
  2222.     }
  2223.     if (Tk_IsTopLevel(slave)) {
  2224.         Tcl_AppendResult(interp, "can't manage \"", argv[j],
  2225.             "\": it's a top-level window", (char *) NULL);
  2226.         return TCL_ERROR;
  2227.     }
  2228.     slavePtr = GetGrid(slave);
  2229.  
  2230.     /*
  2231.      * The following statement is taken from tkPack.c:
  2232.      *
  2233.      * "If the slave isn't currently managed, reset all of its
  2234.      * configuration information to default values (there could
  2235.      * be old values left from a previous packer)."
  2236.      *
  2237.      * I [D.S.] disagree with this statement.  If a slave is disabled (using
  2238.      * "forget") and then re-enabled, I submit that 90% of the time the
  2239.      * programmer will want it to retain its old configuration information.
  2240.      * If the programmer doesn't want this behavior, then the
  2241.      * defaults can be reestablished by hand, without having to worry
  2242.      * about keeping track of the old state. 
  2243.      */
  2244.  
  2245.     for (i = numWindows; i < argc; i+=2) {
  2246.         length = strlen(argv[i]);
  2247.         c = argv[i][1];
  2248.  
  2249.         if (length < 2) {
  2250.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  2251.             argv[i], "\": must be ",
  2252.             "-column, -columnspan, -in, -ipadx, -ipady, ",
  2253.             "-padx, -pady, -row, -rowspan, or -sticky",
  2254.             (char *) NULL);
  2255.         return TCL_ERROR;
  2256.         }
  2257.         if ((c == 'c') && (strncmp(argv[i], "-column", length) == 0)) {
  2258.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
  2259.             Tcl_ResetResult(interp);
  2260.             Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
  2261.                 "\": must be a non-negative integer", (char *)NULL);
  2262.             return TCL_ERROR;
  2263.         }
  2264.         slavePtr->column = tmp;
  2265.         } else if ((c == 'c')
  2266.             && (strncmp(argv[i], "-columnspan", length) == 0)) {
  2267.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp <= 0) {
  2268.             Tcl_ResetResult(interp);
  2269.             Tcl_AppendResult(interp, "bad columnspan value \"", argv[i+1],
  2270.                 "\": must be a positive integer", (char *)NULL);
  2271.             return TCL_ERROR;
  2272.         }
  2273.         slavePtr->numCols = tmp;
  2274.         } else if ((c == 'i') && (strncmp(argv[i], "-in", length) == 0)) {
  2275.         other = Tk_NameToWindow(interp, argv[i+1], tkwin);
  2276.         if (other == NULL) {
  2277.             return TCL_ERROR;
  2278.         }
  2279.         if (other == slave) {
  2280.             sprintf(interp->result,"Window can't be managed in itself");
  2281.             return TCL_ERROR;
  2282.         }
  2283.         masterPtr = GetGrid(other);
  2284.         InitMasterData(masterPtr);
  2285.         } else if ((c == 'i')
  2286.             && (strncmp(argv[i], "-ipadx", length) == 0)) {
  2287.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  2288.             || (tmp < 0)) {
  2289.             Tcl_ResetResult(interp);
  2290.             Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
  2291.                 "\": must be positive screen distance",
  2292.                 (char *) NULL);
  2293.             return TCL_ERROR;
  2294.         }
  2295.         slavePtr->iPadX = tmp*2;
  2296.         } else if ((c == 'i')
  2297.             && (strncmp(argv[i], "-ipady", length) == 0)) {
  2298.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  2299.             || (tmp< 0)) {
  2300.             Tcl_ResetResult(interp);
  2301.             Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
  2302.                 "\": must be positive screen distance",
  2303.                 (char *) NULL);
  2304.             return TCL_ERROR;
  2305.         }
  2306.         slavePtr->iPadY = tmp*2;
  2307.         } else if ((c == 'p')
  2308.             && (strncmp(argv[i], "-padx", length) == 0)) {
  2309.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  2310.             || (tmp< 0)) {
  2311.             Tcl_ResetResult(interp);
  2312.             Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
  2313.                 "\": must be positive screen distance",
  2314.                 (char *) NULL);
  2315.             return TCL_ERROR;
  2316.         }
  2317.         slavePtr->padX = tmp*2;
  2318.         } else if ((c == 'p')
  2319.             && (strncmp(argv[i], "-pady", length) == 0)) {
  2320.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  2321.             || (tmp< 0)) {
  2322.             Tcl_ResetResult(interp);
  2323.             Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
  2324.                 "\": must be positive screen distance",
  2325.                 (char *) NULL);
  2326.             return TCL_ERROR;
  2327.         }
  2328.         slavePtr->padY = tmp*2;
  2329.         } else if ((c == 'r') && (strncmp(argv[i], "-row", length) == 0)) {
  2330.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
  2331.             Tcl_ResetResult(interp);
  2332.             Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
  2333.                 "\": must be a non-negative integer", (char *)NULL);
  2334.             return TCL_ERROR;
  2335.         }
  2336.         slavePtr->row = tmp;
  2337.         } else if ((c == 'r')
  2338.             && (strncmp(argv[i], "-rowspan", length) == 0)) {
  2339.         if ((Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) || tmp<=0) {
  2340.             Tcl_ResetResult(interp);
  2341.             Tcl_AppendResult(interp, "bad rowspan value \"", argv[i+1],
  2342.                 "\": must be a positive integer", (char *)NULL);
  2343.             return TCL_ERROR;
  2344.         }
  2345.         slavePtr->numRows = tmp;
  2346.         } else if ((c == 's')
  2347.             && strncmp(argv[i], "-sticky", length) == 0) {
  2348.         int sticky = StringToSticky(argv[i+1]);
  2349.         if (sticky == -1) {
  2350.             Tcl_AppendResult(interp, "bad stickyness value \"", argv[i+1],
  2351.                 "\": must be a string containing n, e, s, and/or w",
  2352.                 (char *)NULL);
  2353.             return TCL_ERROR;
  2354.         }
  2355.         slavePtr->sticky = sticky;
  2356.         } else {
  2357.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  2358.             argv[i], "\": must be ",
  2359.             "-column, -columnspan, -in, -ipadx, -ipady, ",
  2360.             "-padx, -pady, -row, -rowspan, or -sticky",
  2361.             (char *) NULL);
  2362.         return TCL_ERROR;
  2363.         }
  2364.     }
  2365.  
  2366.     /*
  2367.      * Make sure we have a geometry master.  We look at:
  2368.      *  1)   the -in flag
  2369.      *  2)   the geometry master of the first slave (if specified)
  2370.      *  3)   the parent of the first slave.
  2371.      */
  2372.     
  2373.         if (masterPtr == NULL) {
  2374.         masterPtr = slavePtr->masterPtr;
  2375.         }
  2376.     parent = Tk_Parent(slave);
  2377.         if (masterPtr == NULL) {
  2378.         masterPtr = GetGrid(parent);
  2379.         InitMasterData(masterPtr);
  2380.         }
  2381.  
  2382.     if (slavePtr->masterPtr != NULL && slavePtr->masterPtr != masterPtr) {
  2383.         Unlink(slavePtr);
  2384.         slavePtr->masterPtr = NULL;
  2385.     }
  2386.  
  2387.     if (slavePtr->masterPtr == NULL) {
  2388.         Gridder *tempPtr = masterPtr->slavePtr;
  2389.         slavePtr->masterPtr = masterPtr;
  2390.         masterPtr->slavePtr = slavePtr;
  2391.         slavePtr->nextPtr = tempPtr;
  2392.     }
  2393.  
  2394.     /*
  2395.      * Make sure that the slave's parent is either the master or
  2396.      * an ancestor of the master, and that the master and slave
  2397.      * aren't the same.
  2398.      */
  2399.  
  2400.     for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
  2401.         if (ancestor == parent) {
  2402.         break;
  2403.         }
  2404.         if (Tk_IsTopLevel(ancestor)) {
  2405.         Tcl_AppendResult(interp, "can't put ", argv[j],
  2406.             " inside ", Tk_PathName(masterPtr->tkwin),
  2407.             (char *) NULL);
  2408.         Unlink(slavePtr);
  2409.         return TCL_ERROR;
  2410.         }
  2411.     }
  2412.  
  2413.     /*
  2414.      * Try to make sure our master isn't managed by us.
  2415.      */
  2416.  
  2417.          if (masterPtr->masterPtr == slavePtr) {
  2418.         Tcl_AppendResult(interp, "can't put ", argv[j],
  2419.             " inside ", Tk_PathName(masterPtr->tkwin),
  2420.             ", would cause management loop.", 
  2421.             (char *) NULL);
  2422.         Unlink(slavePtr);
  2423.         return TCL_ERROR;
  2424.          }
  2425.  
  2426.     Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
  2427.  
  2428.     /*
  2429.      * Assign default position information.
  2430.      */
  2431.  
  2432.     if (slavePtr->column == -1) {
  2433.         slavePtr->column = defaultColumn;
  2434.     }
  2435.     slavePtr->numCols += defaultColumnSpan - 1;
  2436.     if (slavePtr->row == -1) {
  2437.         if (masterPtr->masterDataPtr == NULL) {
  2438.             slavePtr->row = 0;
  2439.         } else {
  2440.             slavePtr->row = masterPtr->masterDataPtr->rowEnd;
  2441.         }
  2442.     }
  2443.     defaultColumn += slavePtr->numCols;
  2444.     defaultColumnSpan = 1;
  2445.  
  2446.     /*
  2447.      * Arrange for the parent to be re-arranged at the first
  2448.      * idle moment.
  2449.      */
  2450.  
  2451.     if (masterPtr->abortPtr != NULL) {
  2452.         *masterPtr->abortPtr = 1;
  2453.     }
  2454.     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  2455.         masterPtr->flags |= REQUESTED_RELAYOUT;
  2456.         Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  2457.     }
  2458.     }
  2459.  
  2460.     /* Now look for all the "^"'s. */
  2461.  
  2462.     lastWindow = NULL;
  2463.     for (j = 0; j < numWindows; j++) {
  2464.     struct Gridder *otherPtr;
  2465.     int match;            /* found a match for the ^ */
  2466.     int lastRow, lastColumn;        /* implied end of table */
  2467.  
  2468.         if (*argv[j] == '.') {
  2469.         lastWindow = argv[j];
  2470.     }
  2471.     if (*argv[j] != REL_VERT) {
  2472.         continue;
  2473.     }
  2474.  
  2475.     if (masterPtr == NULL) {
  2476.         Tcl_AppendResult(interp, "can't use '^', cant find master",
  2477.             (char *) NULL);
  2478.         return TCL_ERROR;
  2479.     }
  2480.  
  2481.     for (width=1; width+j < numWindows && *argv[j+width] == REL_VERT;
  2482.         width++) {
  2483.         /* Null Body */
  2484.     }
  2485.  
  2486.     /*
  2487.      * Find the implied grid location of the ^
  2488.      */
  2489.  
  2490.     if (lastWindow == NULL) { 
  2491.         if (masterPtr->masterDataPtr != NULL) {
  2492.         SetGridSize(masterPtr);
  2493.         lastRow = masterPtr->masterDataPtr->rowEnd - 1;
  2494.         } else {
  2495.         lastRow = 0;
  2496.         }
  2497.         lastColumn = 0;
  2498.     } else {
  2499.         other = Tk_NameToWindow(interp, lastWindow, tkwin);
  2500.         otherPtr = GetGrid(other);
  2501.         lastRow = otherPtr->row;
  2502.         lastColumn = otherPtr->column + otherPtr->numCols;
  2503.     }
  2504.  
  2505.     for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  2506.                      slavePtr = slavePtr->nextPtr) {
  2507.  
  2508.         if (slavePtr->numCols == width
  2509.             && slavePtr->column == lastColumn
  2510.             && slavePtr->row + slavePtr->numRows == lastRow) {
  2511.         slavePtr->numRows++;
  2512.         match++;
  2513.         }
  2514.         lastWindow = Tk_PathName(slavePtr->tkwin);
  2515.     }
  2516.     if (!match) {
  2517.         Tcl_AppendResult(interp, "can't find slave to extend with \"^\".",
  2518.             (char *) NULL);
  2519.         return TCL_ERROR;
  2520.     }
  2521.     j += width - 1;
  2522.     }
  2523.  
  2524.     if (masterPtr == NULL) {
  2525.     Tcl_AppendResult(interp, "can't determine master window",
  2526.         (char *) NULL);
  2527.     return TCL_ERROR;
  2528.     }
  2529.     SetGridSize(masterPtr);
  2530.     return TCL_OK;
  2531. }
  2532.  
  2533. /*
  2534.  *----------------------------------------------------------------------
  2535.  *
  2536.  * StickyToString
  2537.  *
  2538.  *    Converts the internal boolean combination of "sticky" bits onto
  2539.  *    a TCL list element containing zero or mor of n, s, e, or w.
  2540.  *
  2541.  * Results:
  2542.  *    A string is placed into the "result" pointer.
  2543.  *
  2544.  * Side effects:
  2545.  *    none.
  2546.  *
  2547.  *----------------------------------------------------------------------
  2548.  */
  2549.  
  2550. static void
  2551. StickyToString(flags, result)
  2552.     int flags;        /* the sticky flags */
  2553.     char *result;    /* where to put the result */
  2554. {
  2555.     int count = 0;
  2556.     if (flags&STICK_NORTH) {
  2557.         result[count++] = 'n';
  2558.     }
  2559.     if (flags&STICK_EAST) {
  2560.         result[count++] = 'e';
  2561.     }
  2562.     if (flags&STICK_SOUTH) {
  2563.         result[count++] = 's';
  2564.     }
  2565.     if (flags&STICK_WEST) {
  2566.         result[count++] = 'w';
  2567.     }
  2568.     if (count) {
  2569.     result[count] = '\0';
  2570.     } else {
  2571.     sprintf(result,"{}");
  2572.     }
  2573. }
  2574.  
  2575. /*
  2576.  *----------------------------------------------------------------------
  2577.  *
  2578.  * StringToSticky --
  2579.  *
  2580.  *    Converts an ascii string representing a widgets stickyness
  2581.  *    into the boolean result.
  2582.  *
  2583.  * Results:
  2584.  *    The boolean combination of the "sticky" bits is retuned.  If an
  2585.  *    error occurs, such as an invalid character, -1 is returned instead.
  2586.  *
  2587.  * Side effects:
  2588.  *    none
  2589.  *
  2590.  *----------------------------------------------------------------------
  2591.  */
  2592.  
  2593. static int
  2594. StringToSticky(string)
  2595.     char *string;
  2596. {
  2597.     int sticky = 0;
  2598.     char c;
  2599.  
  2600.     while ((c = *string++) != '\0') {
  2601.     switch (c) {
  2602.         case 'n': case 'N': sticky |= STICK_NORTH; break;
  2603.         case 'e': case 'E': sticky |= STICK_EAST;  break;
  2604.         case 's': case 'S': sticky |= STICK_SOUTH; break;
  2605.         case 'w': case 'W': sticky |= STICK_WEST;  break;
  2606.         case ' ': case ',': case '\t': case '\r': case '\n': break;
  2607.         default: return -1;
  2608.     }
  2609.     }
  2610.     return sticky;
  2611. }        
  2612.